801 changes: 352 additions & 449 deletions src/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

module ddmd.traits;

import core.stdc.stdio;
import core.stdc.string;
import ddmd.aggregate;
import ddmd.arraytypes;
Expand Down Expand Up @@ -40,199 +41,8 @@ import ddmd.visitor;

enum LOGSEMANTIC = false;

/**
* Collects all unit test functions from the given array of symbols.
*
* This is a helper function used by the implementation of __traits(getUnitTests).
*
* Input:
* symbols array of symbols to collect the functions from
* uniqueUnitTests an associative array (should actually be a set) to
* keep track of already collected functions. We're
* using an AA here to avoid doing a linear search of unitTests
*
* Output:
* unitTests array of DsymbolExp's of the collected unit test functions
* uniqueUnitTests updated with symbols from unitTests[ ]
*/
extern (C++) static void collectUnitTests(Dsymbols* symbols, AA* uniqueUnitTests, Expressions* unitTests)
{
if (!symbols)
return;
for (size_t i = 0; i < symbols.dim; i++)
{
Dsymbol symbol = (*symbols)[i];
UnitTestDeclaration unitTest = symbol.isUnitTestDeclaration();
if (unitTest)
{
if (!dmd_aaGetRvalue(uniqueUnitTests, cast(void*)unitTest))
{
auto ad = new FuncAliasDeclaration(unitTest.ident, unitTest, 0);
ad.protection = unitTest.protection;
Expression e = new DsymbolExp(Loc(), ad);
unitTests.push(e);
bool* value = cast(bool*)dmd_aaGet(&uniqueUnitTests, cast(void*)unitTest);
*value = true;
}
}
else
{
AttribDeclaration attrDecl = symbol.isAttribDeclaration();
if (attrDecl)
{
Dsymbols* decl = attrDecl.include(null, null);
collectUnitTests(decl, uniqueUnitTests, unitTests);
}
}
}
}

/************************ TraitsExp ************************************/

extern (C++) bool isTypeArithmetic(Type t)
{
return t.isintegral() || t.isfloating();
}

extern (C++) bool isTypeFloating(Type t)
{
return t.isfloating();
}

extern (C++) bool isTypeIntegral(Type t)
{
return t.isintegral();
}

extern (C++) bool isTypeScalar(Type t)
{
return t.isscalar();
}

extern (C++) bool isTypeUnsigned(Type t)
{
return t.isunsigned();
}

extern (C++) bool isTypeAssociativeArray(Type t)
{
return t.toBasetype().ty == Taarray;
}

extern (C++) bool isTypeStaticArray(Type t)
{
return t.toBasetype().ty == Tsarray;
}

extern (C++) bool isTypeAbstractClass(Type t)
{
return t.toBasetype().ty == Tclass && (cast(TypeClass)t.toBasetype()).sym.isAbstract();
}

extern (C++) bool isTypeFinalClass(Type t)
{
return t.toBasetype().ty == Tclass && ((cast(TypeClass)t.toBasetype()).sym.storage_class & STCfinal) != 0;
}

extern (C++) Expression isTypeX(TraitsExp e, bool function(Type t) fp)
{
int result = 0;
if (!e.args || !e.args.dim)
goto Lfalse;
for (size_t i = 0; i < e.args.dim; i++)
{
Type t = getType((*e.args)[i]);
if (!t || !fp(t))
goto Lfalse;
}
result = 1;
Lfalse:
return new IntegerExp(e.loc, result, Type.tbool);
}

extern (C++) bool isFuncAbstractFunction(FuncDeclaration f)
{
return f.isAbstract();
}

extern (C++) bool isFuncVirtualFunction(FuncDeclaration f)
{
return f.isVirtual();
}

extern (C++) bool isFuncVirtualMethod(FuncDeclaration f)
{
return f.isVirtualMethod();
}

extern (C++) bool isFuncFinalFunction(FuncDeclaration f)
{
return f.isFinalFunc();
}

extern (C++) bool isFuncStaticFunction(FuncDeclaration f)
{
return !f.needThis() && !f.isNested();
}

extern (C++) bool isFuncOverrideFunction(FuncDeclaration f)
{
return f.isOverride();
}

extern (C++) Expression isFuncX(TraitsExp e, bool function(FuncDeclaration f) fp)
{
int result = 0;
if (!e.args || !e.args.dim)
goto Lfalse;
for (size_t i = 0; i < e.args.dim; i++)
{
Dsymbol s = getDsymbol((*e.args)[i]);
if (!s)
goto Lfalse;
FuncDeclaration f = s.isFuncDeclaration();
if (!f || !fp(f))
goto Lfalse;
}
result = 1;
Lfalse:
return new IntegerExp(e.loc, result, Type.tbool);
}

extern (C++) bool isDeclRef(Declaration d)
{
return d.isRef();
}

extern (C++) bool isDeclOut(Declaration d)
{
return d.isOut();
}

extern (C++) bool isDeclLazy(Declaration d)
{
return (d.storage_class & STClazy) != 0;
}

extern (C++) Expression isDeclX(TraitsExp e, bool function(Declaration d) fp)
{
int result = 0;
if (!e.args || !e.args.dim)
goto Lfalse;
for (size_t i = 0; i < e.args.dim; i++)
{
Dsymbol s = getDsymbol((*e.args)[i]);
if (!s)
goto Lfalse;
Declaration d = s.isDeclaration();
if (!d || !fp(d))
goto Lfalse;
}
result = 1;
Lfalse:
return new IntegerExp(e.loc, result, Type.tbool);
}

// callback for TypeFunction::attributesApply
struct PushAttributes
{
Expand All @@ -246,86 +56,61 @@ struct PushAttributes
}
}

extern (C++) __gshared const(char)** traits =
[
"isAbstractClass",
"isArithmetic",
"isAssociativeArray",
"isFinalClass",
"isPOD",
"isNested",
"isFloating",
"isIntegral",
"isScalar",
"isStaticArray",
"isUnsigned",
"isVirtualFunction",
"isVirtualMethod",
"isAbstractFunction",
"isFinalFunction",
"isOverrideFunction",
"isStaticFunction",
"isRef",
"isOut",
"isLazy",
"hasMember",
"identifier",
"getProtection",
"parent",
"getMember",
"getOverloads",
"getVirtualFunctions",
"getVirtualMethods",
"classInstanceSize",
"allMembers",
"derivedMembers",
"isSame",
"compiles",
"parameters",
"getAliasThis",
"getAttributes",
"getFunctionAttributes",
"getUnitTests",
"getVirtualIndex",
"getPointerBitmap",
null
];
extern (C++) __gshared StringTable traitsStringTable;

extern (C++) void initTraitsStringTable()
static this()
{
traitsStringTable._init(40);
for (size_t idx = 0;; idx++)
{
const(char)* s = traits[idx];
if (!s)
break;
StringValue* sv = traitsStringTable.insert(s, strlen(s));
sv.ptrvalue = cast(void*)traits[idx];
}
}
static immutable string[] names =
[
"isAbstractClass",
"isArithmetic",
"isAssociativeArray",
"isFinalClass",
"isPOD",
"isNested",
"isFloating",
"isIntegral",
"isScalar",
"isStaticArray",
"isUnsigned",
"isVirtualFunction",
"isVirtualMethod",
"isAbstractFunction",
"isFinalFunction",
"isOverrideFunction",
"isStaticFunction",
"isRef",
"isOut",
"isLazy",
"hasMember",
"identifier",
"getProtection",
"parent",
"getMember",
"getOverloads",
"getVirtualFunctions",
"getVirtualMethods",
"classInstanceSize",
"allMembers",
"derivedMembers",
"isSame",
"compiles",
"parameters",
"getAliasThis",
"getAttributes",
"getFunctionAttributes",
"getUnitTests",
"getVirtualIndex",
"getPointerBitmap",
];

extern (C++) bool isTemplate(Dsymbol s)
{
if (!s.toAlias().isOverloadable())
return false;
return overloadApply(s, sm => sm.isTemplateDeclaration() !is null) != 0;
}
traitsStringTable._init(40);

extern (C++) Expression isSymbolX(TraitsExp e, bool function(Dsymbol s) fp)
{
int result = 0;
if (!e.args || !e.args.dim)
goto Lfalse;
for (size_t i = 0; i < e.args.dim; i++)
foreach (s; names)
{
Dsymbol s = getDsymbol((*e.args)[i]);
if (!s || !fp(s))
goto Lfalse;
auto sv = traitsStringTable.insert(s.ptr, s.length);
sv.ptrvalue = cast(void*)s.ptr;
}
result = 1;
Lfalse:
return new IntegerExp(e.loc, result, Type.tbool);
}

/**
Expand All @@ -345,19 +130,23 @@ extern (C++) Expression pointerBitmap(TraitsExp e)
error(e.loc, "a single type expected for trait pointerBitmap");
return new ErrorExp();
}

Type t = getType((*e.args)[0]);
if (!t)
{
error(e.loc, "%s is not a type", (*e.args)[0].toChars());
return new ErrorExp();
}

d_uns64 sz = t.size(e.loc);
if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration())
sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(e.loc);

d_uns64 sz_size_t = Type.tsize_t.size(e.loc);
d_uns64 bitsPerWord = sz_size_t * 8;
d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t;
d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;

Array!(d_uns64) data;
data.setDim(cast(size_t)cntdata);
data.zero();
Expand Down Expand Up @@ -509,9 +298,8 @@ extern (C++) Expression pointerBitmap(TraitsExp e)
override void visit(TypeStruct t)
{
d_uns64 structoff = offset;
for (size_t i = 0; i < t.sym.fields.dim; i++)
foreach (v; t.sym.fields)
{
VarDeclaration v = t.sym.fields[i];
offset = structoff + v.offset;
if (v.type.ty == Tclass)
setpointer(offset);
Expand All @@ -528,9 +316,8 @@ extern (C++) Expression pointerBitmap(TraitsExp e)
// skip vtable-ptr and monitor
if (t.sym.baseClass)
visitClass(cast(TypeClass)t.sym.baseClass.type);
for (size_t i = 0; i < t.sym.fields.dim; i++)
foreach (v; t.sym.fields)
{
VarDeclaration v = t.sym.fields[i];
offset = classoff + v.offset;
v.type.accept(this);
}
Expand All @@ -547,10 +334,12 @@ extern (C++) Expression pointerBitmap(TraitsExp e)
pbv.visitClass(cast(TypeClass)t);
else
t.accept(pbv);

auto exps = new Expressions();
exps.push(new IntegerExp(e.loc, sz, Type.tsize_t));
for (d_uns64 i = 0; i < cntdata; i++)
foreach (d_uns64 i; 0 .. cntdata)
exps.push(new IntegerExp(e.loc, data[cast(size_t)i], Type.tsize_t));

auto ale = new ArrayLiteralExp(e.loc, exps);
ale.type = Type.tsize_t.sarrayOf(cntdata + 1);
return ale;
Expand All @@ -562,66 +351,117 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
{
printf("TraitsExp::semantic() %s\n", e.toChars());
}
if (e.ident != Id.compiles && e.ident != Id.isSame && e.ident != Id.identifier && e.ident != Id.getProtection)

if (e.ident != Id.compiles &&
e.ident != Id.isSame &&
e.ident != Id.identifier &&
e.ident != Id.getProtection)
{
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
return new ErrorExp();
}
size_t dim = e.args ? e.args.dim : 0;

Expression isX(T)(bool function(T) fp)
{
int result = 0;
if (!dim)
goto Lfalse;
foreach (o; *e.args)
{
static if (is(T == Type))
auto y = getType(o);

static if (is(T : Dsymbol))
{
auto s = getDsymbol(o);
if (!s)
goto Lfalse;
}
static if (is(T == Dsymbol))
alias y = s;
static if (is(T == Declaration))
auto y = s.isDeclaration();
static if (is(T == FuncDeclaration))
auto y = s.isFuncDeclaration();

if (!y || !fp(y))
goto Lfalse;
}
result = 1;

Lfalse:
return new IntegerExp(e.loc, result, Type.tbool);
}
alias isTypeX = isX!Type;
alias isDsymX = isX!Dsymbol;
alias isDeclX = isX!Declaration;
alias isFuncX = isX!FuncDeclaration;

if (e.ident == Id.isArithmetic)
{
return isTypeX(e, &isTypeArithmetic);
return isTypeX(t => t.isintegral() || t.isfloating());
}
else if (e.ident == Id.isFloating)
if (e.ident == Id.isFloating)
{
return isTypeX(e, &isTypeFloating);
return isTypeX(t => t.isfloating());
}
else if (e.ident == Id.isIntegral)
if (e.ident == Id.isIntegral)
{
return isTypeX(e, &isTypeIntegral);
return isTypeX(t => t.isintegral());
}
else if (e.ident == Id.isScalar)
if (e.ident == Id.isScalar)
{
return isTypeX(e, &isTypeScalar);
return isTypeX(t => t.isscalar());
}
else if (e.ident == Id.isUnsigned)
if (e.ident == Id.isUnsigned)
{
return isTypeX(e, &isTypeUnsigned);
return isTypeX(t => t.isunsigned());
}
else if (e.ident == Id.isAssociativeArray)
if (e.ident == Id.isAssociativeArray)
{
return isTypeX(e, &isTypeAssociativeArray);
return isTypeX(t => t.toBasetype().ty == Taarray);
}
else if (e.ident == Id.isStaticArray)
if (e.ident == Id.isStaticArray)
{
return isTypeX(e, &isTypeStaticArray);
return isTypeX(t => t.toBasetype().ty == Tsarray);
}
else if (e.ident == Id.isAbstractClass)
if (e.ident == Id.isAbstractClass)
{
return isTypeX(e, &isTypeAbstractClass);
return isTypeX(t => t.toBasetype().ty == Tclass &&
(cast(TypeClass)t.toBasetype()).sym.isAbstract());
}
else if (e.ident == Id.isFinalClass)
if (e.ident == Id.isFinalClass)
{
return isTypeX(e, &isTypeFinalClass);
return isTypeX(t => t.toBasetype().ty == Tclass &&
((cast(TypeClass)t.toBasetype()).sym.storage_class & STCfinal) != 0);
}
else if (e.ident == Id.isTemplate)
if (e.ident == Id.isTemplate)
{
return isSymbolX(e, &isTemplate);
return isDsymX((s)
{
if (!s.toAlias().isOverloadable())
return false;
return overloadApply(s,
sm => sm.isTemplateDeclaration() !is null) != 0;
});
}
else if (e.ident == Id.isPOD)
if (e.ident == Id.isPOD)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Type t = isType(o);
StructDeclaration sd;

auto o = (*e.args)[0];
auto t = isType(o);
if (!t)
{
e.error("type expected as second argument of __traits %s instead of %s", e.ident.toChars(), o.toChars());
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();
if (tb.ty == Tstruct && ((sd = cast(StructDeclaration)(cast(TypeStruct)tb).sym) !is null))
if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
{
if (sd.isPOD())
goto Ltrue;
Expand All @@ -630,71 +470,71 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
}
goto Ltrue;
}
else if (e.ident == Id.isNested)
if (e.ident == Id.isNested)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);
AggregateDeclaration a;
FuncDeclaration f;

auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (!s)
{
}
else if ((a = s.isAggregateDeclaration()) !is null)
else if (auto ad = s.isAggregateDeclaration())
{
if (a.isNested())
if (ad.isNested())
goto Ltrue;
else
goto Lfalse;
}
else if ((f = s.isFuncDeclaration()) !is null)
else if (auto fd = s.isFuncDeclaration())
{
if (f.isNested())
if (fd.isNested())
goto Ltrue;
else
goto Lfalse;
}

e.error("aggregate or function expected instead of '%s'", o.toChars());
return new ErrorExp();
}
else if (e.ident == Id.isAbstractFunction)
if (e.ident == Id.isAbstractFunction)
{
return isFuncX(e, &isFuncAbstractFunction);
return isFuncX(f => f.isAbstract());
}
else if (e.ident == Id.isVirtualFunction)
if (e.ident == Id.isVirtualFunction)
{
return isFuncX(e, &isFuncVirtualFunction);
return isFuncX(f => f.isVirtual());
}
else if (e.ident == Id.isVirtualMethod)
if (e.ident == Id.isVirtualMethod)
{
return isFuncX(e, &isFuncVirtualMethod);
return isFuncX(f => f.isVirtualMethod());
}
else if (e.ident == Id.isFinalFunction)
if (e.ident == Id.isFinalFunction)
{
return isFuncX(e, &isFuncFinalFunction);
return isFuncX(f => f.isFinalFunc());
}
else if (e.ident == Id.isOverrideFunction)
if (e.ident == Id.isOverrideFunction)
{
return isFuncX(e, &isFuncOverrideFunction);
return isFuncX(f => f.isOverride());
}
else if (e.ident == Id.isStaticFunction)
if (e.ident == Id.isStaticFunction)
{
return isFuncX(e, &isFuncStaticFunction);
return isFuncX(f => !f.needThis() && !f.isNested());
}
else if (e.ident == Id.isRef)
if (e.ident == Id.isRef)
{
return isDeclX(e, &isDeclRef);
return isDeclX(d => d.isRef());
}
else if (e.ident == Id.isOut)
if (e.ident == Id.isOut)
{
return isDeclX(e, &isDeclOut);
return isDeclX(d => d.isOut());
}
else if (e.ident == Id.isLazy)
if (e.ident == Id.isLazy)
{
return isDeclX(e, &isDeclLazy);
return isDeclX(d => (d.storage_class & STClazy) != 0);
}
else if (e.ident == Id.identifier)
if (e.ident == Id.identifier)
{
// Get identifier for symbol as a string literal
/* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
Expand All @@ -705,10 +545,10 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
return new ErrorExp();
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Parameter po = isParameter(o);

auto o = (*e.args)[0];
Identifier id;
if (po)
if (auto po = isParameter(o))
{
id = po.ident;
assert(id);
Expand All @@ -723,21 +563,24 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
}
id = s.ident;
}

auto se = new StringExp(e.loc, id.toChars());
return se.semantic(sc);
}
else if (e.ident == Id.getProtection)
if (e.ident == Id.getProtection)
{
if (dim != 1)
goto Ldimerror;

Scope* sc2 = sc.push();
sc2.flags = sc.flags | SCOPEnoaccesscheck;
bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
sc2.pop();
if (!ok)
return new ErrorExp();
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);

auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (!s)
{
if (!isError(o))
Expand All @@ -746,20 +589,22 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
}
if (s._scope)
s.semantic(s._scope);
const(char)* protName = protectionToChars(s.prot().kind); // TODO: How about package(names)

auto protName = protectionToChars(s.prot().kind); // TODO: How about package(names)
assert(protName);
auto se = new StringExp(e.loc, cast(char*)protName);
return se.semantic(sc);
}
else if (e.ident == Id.parent)
if (e.ident == Id.parent)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);

auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (s)
{
if (FuncDeclaration fd = s.isFuncDeclaration()) // Bugzilla 8943
if (auto fd = s.isFuncDeclaration()) // Bugzilla 8943
s = fd.toAliasFunc();
if (!s.isImport()) // Bugzilla 8922
s = s.toParent();
Expand All @@ -769,17 +614,18 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
e.error("argument %s has no parent", o.toChars());
return new ErrorExp();
}
if (FuncDeclaration f = s.isFuncDeclaration())

if (auto f = s.isFuncDeclaration())
{
if (TemplateDeclaration td = getFuncTemplateDecl(f))
if (auto td = getFuncTemplateDecl(f))
{
if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
td = td.overroot; // then get the start
Expression ex = new TemplateExp(e.loc, td, f);
ex = ex.semantic(sc);
return ex;
}
if (FuncLiteralDeclaration fld = f.isFuncLiteralDeclaration())
if (auto fld = f.isFuncLiteralDeclaration())
{
// Directly translate to VarExp instead of FuncExp
Expression ex = new VarExp(e.loc, fld, 1);
Expand All @@ -788,31 +634,39 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
}
return DsymbolExp.resolve(e.loc, sc, s, false);
}
else if (e.ident == Id.hasMember || e.ident == Id.getMember || e.ident == Id.getOverloads || e.ident == Id.getVirtualMethods || e.ident == Id.getVirtualFunctions)
if (e.ident == Id.hasMember ||
e.ident == Id.getMember ||
e.ident == Id.getOverloads ||
e.ident == Id.getVirtualMethods ||
e.ident == Id.getVirtualFunctions)
{
if (dim != 2)
goto Ldimerror;
RootObject o = (*e.args)[0];
Expression ex = isExpression((*e.args)[1]);

auto o = (*e.args)[0];
auto ex = isExpression((*e.args)[1]);
if (!ex)
{
e.error("expression expected as second argument of __traits %s", e.ident.toChars());
return new ErrorExp();
}
ex = ex.ctfeInterpret();

StringExp se = ex.toStringExp();
if (!se || se.len == 0)
{
e.error("string expected as second argument of __traits %s instead of %s", e.ident.toChars(), ex.toChars());
return new ErrorExp();
}
se = se.toUTF8(sc);

if (se.sz != 1)
{
e.error("string must be chars");
return new ErrorExp();
}
Identifier id = Identifier.idPool(cast(char*)se.string);

/* Prefer dsymbol, because it might need some runtime contexts.
*/
Dsymbol sym = getDsymbol(o);
Expand All @@ -821,9 +675,9 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
ex = new DsymbolExp(e.loc, sym);
ex = new DotIdExp(e.loc, ex, id);
}
else if (Type t = isType(o))
else if (auto t = isType(o))
ex = typeDotIdExp(e.loc, t, id);
else if (Expression ex2 = isExpression(o))
else if (auto ex2 = isExpression(o))
ex = new DotIdExp(e.loc, ex2, id);
else
{
Expand All @@ -834,10 +688,10 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
{
if (sym)
{
Dsymbol sm = sym.search(e.loc, id);
if (sm)
if (auto sm = sym.search(e.loc, id))
goto Ltrue;
}

/* Take any errors as meaning it wasn't found
*/
Scope* sc2 = sc.push();
Expand Down Expand Up @@ -907,14 +761,15 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
else
assert(0);
}
else if (e.ident == Id.classInstanceSize)
if (e.ident == Id.classInstanceSize)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);
ClassDeclaration cd;
if (!s || (cd = s.isClassDeclaration()) is null)

auto o = (*e.args)[0];
auto s = getDsymbol(o);
auto cd = s ? s.isClassDeclaration() : null;
if (!cd)
{
e.error("first argument is not a class");
return new ErrorExp();
Expand All @@ -929,33 +784,37 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
e.error("%s %s is forward referenced", cd.kind(), cd.toChars());
return new ErrorExp();
}

return new IntegerExp(e.loc, cd.structsize, Type.tsize_t);
}
else if (e.ident == Id.getAliasThis)
if (e.ident == Id.getAliasThis)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);
AggregateDeclaration ad;
if (!s || (ad = s.isAggregateDeclaration()) is null)

auto o = (*e.args)[0];
auto s = getDsymbol(o);
auto ad = s ? s.isAggregateDeclaration() : null;
if (!ad)
{
e.error("argument is not an aggregate type");
return new ErrorExp();
}

auto exps = new Expressions();
if (ad.aliasthis)
exps.push(new StringExp(e.loc, ad.aliasthis.ident.toChars()));
Expression ex = new TupleExp(e.loc, exps);
ex = ex.semantic(sc);
return ex;
}
else if (e.ident == Id.getAttributes)
if (e.ident == Id.getAttributes)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);

auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (!s)
{
version (none)
Expand All @@ -970,30 +829,33 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
e.error("first argument is not a symbol");
return new ErrorExp();
}
if (s.isImport())
if (auto imp = s.isImport())
{
s = s.isImport().mod;
s = imp.mod;
}

//printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->scope);
UserAttributeDeclaration udad = s.userAttribDecl;
auto tup = new TupleExp(e.loc, udad ? udad.getAttributes() : new Expressions());
auto udad = s.userAttribDecl;
auto exps = udad ? udad.getAttributes() : new Expressions();
auto tup = new TupleExp(e.loc, exps);
return tup.semantic(sc);
}
else if (e.ident == Id.getFunctionAttributes)
if (e.ident == Id.getFunctionAttributes)
{
/// extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
// extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);
Type t = isType(o);

auto o = (*e.args)[0];
auto s = getDsymbol(o);
auto t = isType(o);
TypeFunction tf = null;
if (s)
{
if (FuncDeclaration f = s.isFuncDeclaration())
t = f.type;
else if (VarDeclaration v = s.isVarDeclaration())
t = v.type;
if (auto fd = s.isFuncDeclaration())
t = fd.type;
else if (auto vd = s.isVarDeclaration())
t = vd.type;
}
if (t)
{
Expand All @@ -1009,31 +871,36 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
e.error("first argument is not a function");
return new ErrorExp();
}

auto mods = new Expressions();
PushAttributes pa;
pa.mods = mods;
tf.modifiersApply(&pa, &PushAttributes.fp);
tf.attributesApply(&pa, &PushAttributes.fp, TRUSTformatSystem);

auto tup = new TupleExp(e.loc, mods);
return tup.semantic(sc);
}
else if (e.ident == Id.allMembers || e.ident == Id.derivedMembers)
if (e.ident == Id.allMembers ||
e.ident == Id.derivedMembers)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);

auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (!s)
{
e.error("argument has no members");
return new ErrorExp();
}
if (Import imp = s.isImport())
if (auto imp = s.isImport())
{
// Bugzilla 9692
s = imp.mod;
}
ScopeDsymbol sds = s.isScopeDsymbol();

auto sds = s.isScopeDsymbol();
if (!sds || sds.isTemplateDeclaration())
{
e.error("%s %s has no members", s.kind(), s.toChars());
Expand Down Expand Up @@ -1067,11 +934,11 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
return 0;

//printf("\t%s\n", sm->ident->toChars());

/* Skip if already present in idents[]
*/
for (size_t j = 0; j < idents.dim; j++)
foreach (id; *idents)
{
Identifier id = (*idents)[j];
if (id == sm.ident)
return 0;

Expand All @@ -1080,19 +947,15 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
}
idents.push(sm.ident);
}
else
else if (auto ed = sm.isEnumDeclaration())
{
EnumDeclaration ed = sm.isEnumDeclaration();
if (ed)
{
ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
}
ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
}
return 0;
}

ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
ClassDeclaration cd = sds.isClassDeclaration();
auto cd = sds.isClassDeclaration();
if (cd && e.ident == Id.allMembers)
{
if (cd._scope)
Expand All @@ -1102,7 +965,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
{
for (size_t i = 0; i < cd.baseclasses.dim; i++)
{
ClassDeclaration cb = (*cd.baseclasses)[i].sym;
auto cb = (*cd.baseclasses)[i].sym;
assert(cb);
ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
if (cb.baseclasses.dim)
Expand All @@ -1112,15 +975,16 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)

pushBaseMembersDg(cd);
}

// Turn Identifiers into StringExps reusing the allocated array
assert(Expressions.sizeof == Identifiers.sizeof);
Expressions* exps = cast(Expressions*)idents;
for (size_t i = 0; i < idents.dim; i++)
auto exps = cast(Expressions*)idents;
foreach (i, id; *idents)
{
Identifier id = (*idents)[i];
auto se = new StringExp(e.loc, id.toChars());
(*exps)[i] = se;
}

/* Making this a tuple is more flexible, as it can be statically unrolled.
* To make an array literal, enclose __traits in [ ]:
* [ __traits(allMembers, ...) ]
Expand All @@ -1129,24 +993,26 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
ex = ex.semantic(sc);
return ex;
}
else if (e.ident == Id.compiles)
if (e.ident == Id.compiles)
{
/* Determine if all the objects - types, expressions, or symbols -
* compile without error
*/
if (!dim)
goto Lfalse;
for (size_t i = 0; i < dim; i++)

foreach (o; *e.args)
{
uint errors = global.startGagging();
Scope* sc2 = sc.push();
sc2.tinst = null;
sc2.minst = null;
sc2.flags = (sc.flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile;

bool err = false;
RootObject o = (*e.args)[i];
Type t = isType(o);
Expression ex = t ? t.toExpression() : isExpression(o);

auto t = isType(o);
auto ex = t ? t.toExpression() : isExpression(o);
if (!ex && t)
{
Dsymbol s;
Expand All @@ -1167,45 +1033,47 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
ex = ex.optimize(WANTvalue);
if (sc2.func && sc2.func.type.ty == Tfunction)
{
TypeFunction tf = cast(TypeFunction)sc2.func.type;
auto tf = cast(TypeFunction)sc2.func.type;
canThrow(ex, sc2.func, tf.isnothrow);
}
ex = checkGC(sc2, ex);
if (ex.op == TOKerror)
err = true;
}

sc2.pop();

if (global.endGagging(errors) || err)
{
goto Lfalse;
}
}
goto Ltrue;
}
else if (e.ident == Id.isSame)
if (e.ident == Id.isSame)
{
/* Determine if two symbols are the same
*/
if (dim != 2)
goto Ldimerror;

if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 0))
return new ErrorExp();
RootObject o1 = (*e.args)[0];
RootObject o2 = (*e.args)[1];
Dsymbol s1 = getDsymbol(o1);
Dsymbol s2 = getDsymbol(o2);

auto o1 = (*e.args)[0];
auto o2 = (*e.args)[1];
auto s1 = getDsymbol(o1);
auto s2 = getDsymbol(o2);
//printf("isSame: %s, %s\n", o1->toChars(), o2->toChars());
version (none)
{
printf("o1: %p\n", o1);
printf("o2: %p\n", o2);
if (!s1)
{
Expression ea = isExpression(o1);
if (ea)
if (auto ea = isExpression(o1))
printf("%s\n", ea.toChars());
Type ta = isType(o1);
if (ta)
if (auto ta = isType(o1))
printf("%s\n", ta.toChars());
goto Lfalse;
}
Expand All @@ -1214,8 +1082,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
}
if (!s1 && !s2)
{
Expression ea1 = isExpression(o1);
Expression ea2 = isExpression(o2);
auto ea1 = isExpression(o1);
auto ea2 = isExpression(o2);
if (ea1 && ea2)
{
if (ea1.equals(ea2))
Expand All @@ -1226,90 +1094,125 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
goto Lfalse;
s1 = s1.toAlias();
s2 = s2.toAlias();
if (s1.isFuncAliasDeclaration())
s1 = (cast(FuncAliasDeclaration)s1).toAliasFunc();
if (s2.isFuncAliasDeclaration())
s2 = (cast(FuncAliasDeclaration)s2).toAliasFunc();

if (auto fa1 = s1.isFuncAliasDeclaration())
s1 = fa1.toAliasFunc();
if (auto fa2 = s2.isFuncAliasDeclaration())
s2 = fa2.toAliasFunc();

if (s1 == s2)
goto Ltrue;
else
goto Lfalse;
}
else if (e.ident == Id.getUnitTests)
if (e.ident == Id.getUnitTests)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);

auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (!s)
{
e.error("argument %s to __traits(getUnitTests) must be a module or aggregate", o.toChars());
e.error("argument %s to __traits(getUnitTests) must be a module or aggregate",
o.toChars());
return new ErrorExp();
}
Import imp = s.isImport();
if (imp) // Bugzilla 10990
if (auto imp = s.isImport()) // Bugzilla 10990
s = imp.mod;
ScopeDsymbol _scope = s.isScopeDsymbol();
if (!_scope)

auto sds = s.isScopeDsymbol();
if (!sds)
{
e.error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", s.toChars(), s.kind());
e.error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s",
s.toChars(), s.kind());
return new ErrorExp();
}
auto unitTests = new Expressions();
Dsymbols* symbols = _scope.members;
if (global.params.useUnitTests && symbols)

auto exps = new Expressions();
if (global.params.useUnitTests)
{
// Should actually be a set
AA* uniqueUnitTests = null;
collectUnitTests(symbols, uniqueUnitTests, unitTests);

void collectUnitTests(Dsymbols* a)
{
if (!a)
return;
foreach (s; *a)
{
if (auto atd = s.isAttribDeclaration())
{
collectUnitTests(atd.include(null, null));
continue;
}
if (auto ud = s.isUnitTestDeclaration())
{
if (dmd_aaGetRvalue(uniqueUnitTests, cast(void*)ud))
continue;

auto ad = new FuncAliasDeclaration(ud.ident, ud, 0);
ad.protection = ud.protection;

auto e = new DsymbolExp(Loc(), ad);
exps.push(e);

auto pv = cast(bool*)dmd_aaGet(&uniqueUnitTests, cast(void*)ud);
*pv = true;
}
}
}
collectUnitTests(sds.members);
}
auto tup = new TupleExp(e.loc, unitTests);
return tup.semantic(sc);
auto te = new TupleExp(e.loc, exps);
return te.semantic(sc);
}
else if (e.ident == Id.getVirtualIndex)
if (e.ident == Id.getVirtualIndex)
{
if (dim != 1)
goto Ldimerror;
RootObject o = (*e.args)[0];
Dsymbol s = getDsymbol(o);
FuncDeclaration fd;
if (!s || (fd = s.isFuncDeclaration()) is null)

auto o = (*e.args)[0];
auto s = getDsymbol(o);

auto fd = s ? s.isFuncDeclaration() : null;
if (!fd)
{
e.error("first argument to __traits(getVirtualIndex) must be a function");
return new ErrorExp();
}

fd = fd.toAliasFunc(); // Neccessary to support multiple overloads.
return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
}
else if (e.ident == Id.getPointerBitmap)
if (e.ident == Id.getPointerBitmap)
{
return pointerBitmap(e);
}
else
{
extern (D) void* trait_search_fp(const(char)* seed, ref int cost)
{
//printf("trait_search_fp('%s')\n", seed);
size_t len = strlen(seed);
if (!len)
return null;
cost = 0;
StringValue* sv = traitsStringTable.lookup(seed, len);
return sv ? cast(void*)sv.ptrvalue : null;
}

if (auto sub = cast(const(char)*)speller(e.ident.toChars(), &trait_search_fp, idchars))
e.error("unrecognized trait '%s', did you mean '%s'?", e.ident.toChars(), sub);
else
e.error("unrecognized trait '%s'", e.ident.toChars());
return new ErrorExp();
extern (D) void* trait_search_fp(const(char)* seed, ref int cost)
{
//printf("trait_search_fp('%s')\n", seed);
size_t len = strlen(seed);
if (!len)
return null;
cost = 0;
StringValue* sv = traitsStringTable.lookup(seed, len);
return sv ? cast(void*)sv.ptrvalue : null;
}
assert(0);

if (auto sub = cast(const(char)*)speller(e.ident.toChars(), &trait_search_fp, idchars))
e.error("unrecognized trait '%s', did you mean '%s'?", e.ident.toChars(), sub);
else
e.error("unrecognized trait '%s'", e.ident.toChars());
return new ErrorExp();

Ldimerror:
e.error("wrong number of arguments %d", cast(int)dim);
return new ErrorExp();

Lfalse:
return new IntegerExp(e.loc, 0, Type.tbool);

Ltrue:
return new IntegerExp(e.loc, 1, Type.tbool);
}