Skip to content

Commit

Permalink
Fix Issue 19180 - Expose dmd.mtype.Type.isZeroInit as __traits(isZero…
Browse files Browse the repository at this point in the history
…Init, T)
  • Loading branch information
n8sh committed Aug 23, 2018
1 parent 22b2418 commit 6983b0b
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 4 deletions.
32 changes: 32 additions & 0 deletions changelog/isZeroInit.dd
@@ -0,0 +1,32 @@
Expose `__traits(isZeroInit, T)`

Takes one argument which must be a type. If the type's
$(DDSUBLINK spec/property, init, default initializer) has no non-zero
bits then `true` is returned, otherwise `false`. `isZeroInit` will
always return `true` for a class `C` because `C.init` is a null
reference.

This property was already being computed internally by the compiler
so exposing it via `__traits` is more efficient than re-implementing
it in library code.

---
struct S1
{
int x;
}

struct S2
{
int x = -1;
}

static assert(__traits(isZeroInit, S1));
static assert(!__traits(isZeroInit, S2));

void test()
{
int x = 3;
static assert(__traits(isZeroInit, typeof(x)));
}
---
104 changes: 101 additions & 3 deletions src/dmd/dstruct.d
Expand Up @@ -401,9 +401,13 @@ extern (C++) class StructDeclaration : AggregateDeclaration
{
if (vd._init)
{
// Should examine init to see if it is really all 0's
zeroInit = false;
break;
// Examine init to see if it is all 0s.
auto exp = vd.getConstInitializer();
if (!exp || !_isZeroInit(vd.type.toBasetype(), exp))
{
zeroInit = false;
break;
}
}
else if (!vd.type.isZeroInit(loc))
{
Expand Down Expand Up @@ -590,6 +594,100 @@ extern (C++) class StructDeclaration : AggregateDeclaration
}
}

private bool _isZeroInit(Type type, Expression exp)
{
if (type.ty == Tsarray)
{
auto sarrayType = cast(TypeSArray) type;
const dim = cast(size_t) sarrayType.dim.toInteger();
auto elementType = type.baseElemOf().toBasetype();
if (exp.op == TOK.arrayLiteral)
{
auto arrayExp = cast(ArrayLiteralExp) exp;
foreach (i; 0 .. dim)
{
auto ei = arrayExp.getElement(i);
if (ei is null || !_isZeroInit(elementType, ei))
{
return false;
}
}
return true;
}
else if (auto stringExp = exp.toStringExp())
{
foreach (i; 0 .. stringExp.len)
if (stringExp.getCodeUnit(i) != 0)
return false;
return true;
}
else
{
// Can this happen?
return dim == 0;
}
}
else if (type.ty == Tstruct)
{
// FIXME: is anything special necessary for unions?
if (exp.op == TOK.structLiteral)
{
auto structLiteralExp = cast(StructLiteralExp) exp;
foreach (i; 0 .. structLiteralExp.sd.fields.dim)
{
auto field = structLiteralExp.sd.fields[i];
if ((*structLiteralExp.elements)[i] is null
? !field.type.toBasetype().isZeroInit(field.loc)
: !_isZeroInit(field.type.toBasetype(), (*structLiteralExp.elements)[i]))
return false;
}
return true;
}
else
{
// Can this happen?
return false;
}
}
else if (type.ty == Tvector)
{
auto vectorType = cast(TypeVector) type;
if (exp.op == TOK.vector)
{
auto vectorExp = cast(VectorExp) exp;
auto e1 = vectorExp.e1;
return e1 !is null && e1.type !is null && _isZeroInit(e1.type, e1);
}
else
{
// Can this happen?
return false;
}
}
else if (exp.op == TOK.arrayLiteral)
{
// Not sure what's going on here but this gets reached with a
// LHS that can't be initialized from an array literal when
// testing Bug10483 in tests/runnable/interpret.d.
return false;
}
else if (type.isintegral) return exp.toInteger() == 0;
else if (type.isfloating)
{
static if (!is(typeof(exp.toReal()) == struct))
return (exp.toReal() is 0) && (exp.toImaginary() is 0);
else
{
// Software floating point.
auto re = exp.toReal(), im = exp.toImaginary();
return ((re.mantissa | im.mantissa) | (re.exp_sign | im.exp_sign)) == 0;
}
}
else if (exp.op == TOK.null_ || exp.op == TOK.false_) return true;
// Nothing else applied.
else return false;
}

/***********************************************************
* Unions are a variation on structs.
*/
Expand Down
1 change: 1 addition & 0 deletions src/dmd/id.d
Expand Up @@ -403,6 +403,7 @@ immutable Msgtable[] msgtable =
{ "getVirtualIndex" },
{ "getPointerBitmap" },
{ "isReturnOnStack" },
{ "isZeroInit" },

// For C++ mangling
{ "allocator" },
Expand Down
20 changes: 19 additions & 1 deletion src/dmd/traits.d
Expand Up @@ -147,9 +147,10 @@ shared static this()
"getUnitTests",
"getVirtualIndex",
"getPointerBitmap",
"isZeroInit",
];

traitsStringTable._init(40);
traitsStringTable._init(48);

foreach (s; names)
{
Expand Down Expand Up @@ -1730,6 +1731,23 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
{
return pointerBitmap(e);
}
if (e.ident == Id.isZeroInit)
{
if (dim != 1)
return dimError(1);

auto o = (*e.args)[0];
Type t = isType(o);
if (!t)
{
e.error("type expected as second argument of __traits `%s` instead of `%s`",
e.ident.toChars(), o.toChars());
return new ErrorExp();
}

Type tb = t.baseElemOf();
return tb.isZeroInit(e.loc) ? True() : False();
}

extern (D) void* trait_search_fp(const(char)* seed, ref int cost)
{
Expand Down
78 changes: 78 additions & 0 deletions test/compilable/isZeroInit.d
@@ -0,0 +1,78 @@
alias AliasSeq(T...) = T;

struct Holder(T, ubyte val)
{
T x = val;
}

struct SArrayHolder(T, ubyte val)
{
T[2] x = val;
}

static foreach (T; AliasSeq!(bool, byte, short, int, long,
ubyte, ushort, uint, ulong,
char, wchar, dchar,
float, double, real))
{
static assert(__traits(isZeroInit, T) == (T.init is T(0)));
static assert(__traits(isZeroInit, T[2]) == (T.init is T(0)));

static assert(!__traits(isZeroInit, Holder!(T, 1)));
static assert(__traits(isZeroInit, Holder!(T, 0)));

static assert(__traits(isZeroInit, SArrayHolder!(T, 0)));
static assert(!__traits(isZeroInit, SArrayHolder!(T, 1)));

}

static assert(__traits(isZeroInit, void)); // For initializing arrays of element type `void`.
static assert(__traits(isZeroInit, void*));
static assert(__traits(isZeroInit, void[]));
static assert(__traits(isZeroInit, float[]));
static assert(__traits(isZeroInit, Object));
class C1 : Object
{
int x = 1;
}
static assert(__traits(isZeroInit, C1)); // An Object's fields are irrelevant.

struct S1
{
int[] a;
int b;
}
static assert(__traits(isZeroInit, S1));

struct S2
{
alias H = Holder!(int, 1);
H h;
int a;
}
static assert(!__traits(isZeroInit, S2));

struct S3
{
S1 h;
float f = 0;
}
static assert(__traits(isZeroInit, S3));

struct S4
{
S2 h = S2(S2.H(0), 0);
int a;
}
static assert(__traits(isZeroInit, S4));

struct S5
{
Object o = null;
}
static assert(__traits(isZeroInit, S5));

version(D_SIMD):
import core.simd : int4;
static assert(__traits(isZeroInit, Holder!(int4, 0)));
static assert(!__traits(isZeroInit, Holder!(int4, 1)));
12 changes: 12 additions & 0 deletions test/fail_compilation/fail_isZeroInit.d
@@ -0,0 +1,12 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail_isZeroInit.d(11): Error: type expected as second argument of __traits `isZeroInit` instead of `a`
---
*/
void test()
{
int a = 3;
// Providing a specific variable rather than a type isn't allowed.
enum bool az = __traits(isZeroInit, a);
}

0 comments on commit 6983b0b

Please sign in to comment.