blob: 4b15e8c4739a4ab5edebbdae4cc714304169345c [file] [log] [blame]
/**
* Handle introspection functionality of the `__traits()` construct.
*
* Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits)
*
* Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d)
* Documentation: https://dlang.org/phobos/dmd_traits.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d
*/
module dmd.traits;
import core.stdc.stdio;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astcodegen;
import dmd.astenums;
import dmd.attrib;
import dmd.canthrow;
import dmd.dclass;
import dmd.declaration;
import dmd.dimport;
import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.nogc;
import dmd.parse;
import dmd.root.array;
import dmd.root.speller;
import dmd.root.stringtable;
import dmd.target;
import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
import dmd.root.rootobject;
import dmd.common.outbuffer;
import dmd.root.string;
enum LOGSEMANTIC = false;
/************************ TraitsExp ************************************/
/**************************************
* Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
* stripping off expression contexts.
*
* Some symbol related `__traits` ignore arguments expression contexts.
* For example:
* ----
* struct S { void f() {} }
* S s;
* pragma(msg, __traits(isNested, s.f));
* // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
* ----
*
* This is used for that common `__traits` behavior.
*
* Input:
* oarg object to get the symbol for
* Returns:
* Dsymbol the corresponding symbol for oarg
*/
private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
{
if (auto e = isExpression(oarg))
{
if (auto dve = e.isDotVarExp())
return dve.var;
if (auto dte = e.isDotTemplateExp())
return dte.td;
}
return getDsymbol(oarg);
}
private const StringTable!bool traitsStringTable;
shared static this()
{
static immutable string[] names =
[
"isAbstractClass",
"isArithmetic",
"isAssociativeArray",
"isDisabled",
"isDeprecated",
"isFuture",
"isFinalClass",
"isPOD",
"isNested",
"isFloating",
"isIntegral",
"isScalar",
"isStaticArray",
"isUnsigned",
"isVirtualFunction",
"isVirtualMethod",
"isAbstractFunction",
"isFinalFunction",
"isOverrideFunction",
"isStaticFunction",
"isModule",
"isPackage",
"isRef",
"isOut",
"isLazy",
"isReturnOnStack",
"hasMember",
"identifier",
"getProtection",
"getVisibility",
"parent",
"child",
"getLinkage",
"getMember",
"getOverloads",
"getVirtualFunctions",
"getVirtualMethods",
"classInstanceSize",
"allMembers",
"derivedMembers",
"isSame",
"compiles",
"getAliasThis",
"getAttributes",
"getFunctionAttributes",
"getFunctionVariadicStyle",
"getParameterStorageClasses",
"getUnitTests",
"getVirtualIndex",
"getPointerBitmap",
"isZeroInit",
"getTargetInfo",
"getLocation",
"hasPostblit",
"hasCopyConstructor",
"isCopyable",
"parameters"
];
StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable;
stringTable._init(names.length);
foreach (s; names)
{
auto sv = stringTable.insert(s, true);
assert(sv);
}
}
/**
* get an array of size_t values that indicate possible pointer words in memory
* if interpreted as the type given as argument
* Returns: the size of the type in bytes, ulong.max on error
*/
ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data)
{
ulong sz;
if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration())
sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(loc);
else
sz = t.size(loc);
if (sz == SIZE_INVALID)
return ulong.max;
const sz_size_t = Type.tsize_t.size(loc);
if (sz > sz.max - sz_size_t)
{
error(loc, "size overflow for type `%s`", t.toChars());
return ulong.max;
}
ulong bitsPerWord = sz_size_t * 8;
ulong cntptr = (sz + sz_size_t - 1) / sz_size_t;
ulong cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
data.setDim(cast(size_t)cntdata);
data.zero();
extern (C++) final class PointerBitmapVisitor : Visitor
{
alias visit = Visitor.visit;
public:
extern (D) this(Array!(ulong)* _data, ulong _sz_size_t)
{
this.data = _data;
this.sz_size_t = _sz_size_t;
}
void setpointer(ulong off)
{
ulong ptroff = off / sz_size_t;
(*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t));
}
override void visit(Type t)
{
Type tb = t.toBasetype();
if (tb != t)
tb.accept(this);
}
override void visit(TypeError t)
{
visit(cast(Type)t);
}
override void visit(TypeNext t)
{
assert(0);
}
override void visit(TypeBasic t)
{
if (t.ty == Tvoid)
setpointer(offset);
}
override void visit(TypeVector t)
{
}
override void visit(TypeArray t)
{
assert(0);
}
override void visit(TypeSArray t)
{
ulong arrayoff = offset;
ulong nextsize = t.next.size();
if (nextsize == SIZE_INVALID)
error = true;
ulong dim = t.dim.toInteger();
for (ulong i = 0; i < dim; i++)
{
offset = arrayoff + i * nextsize;
t.next.accept(this);
}
offset = arrayoff;
}
override void visit(TypeDArray t)
{
setpointer(offset + sz_size_t);
}
// dynamic array is {length,ptr}
override void visit(TypeAArray t)
{
setpointer(offset);
}
override void visit(TypePointer t)
{
if (t.nextOf().ty != Tfunction) // don't mark function pointers
setpointer(offset);
}
override void visit(TypeReference t)
{
setpointer(offset);
}
override void visit(TypeClass t)
{
setpointer(offset);
}
override void visit(TypeFunction t)
{
}
override void visit(TypeDelegate t)
{
setpointer(offset);
}
// delegate is {context, function}
override void visit(TypeQualified t)
{
assert(0);
}
// assume resolved
override void visit(TypeIdentifier t)
{
assert(0);
}
override void visit(TypeInstance t)
{
assert(0);
}
override void visit(TypeTypeof t)
{
assert(0);
}
override void visit(TypeReturn t)
{
assert(0);
}
override void visit(TypeEnum t)
{
visit(cast(Type)t);
}
override void visit(TypeTuple t)
{
visit(cast(Type)t);
}
override void visit(TypeSlice t)
{
assert(0);
}
override void visit(TypeNull t)
{
// always a null pointer
}
override void visit(TypeStruct t)
{
ulong structoff = offset;
foreach (v; t.sym.fields)
{
offset = structoff + v.offset;
if (v.type.ty == Tclass)
setpointer(offset);
else
v.type.accept(this);
}
offset = structoff;
}
// a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
void visitClass(TypeClass t)
{
ulong classoff = offset;
// skip vtable-ptr and monitor
if (t.sym.baseClass)
visitClass(cast(TypeClass)t.sym.baseClass.type);
foreach (v; t.sym.fields)
{
offset = classoff + v.offset;
v.type.accept(this);
}
offset = classoff;
}
Array!(ulong)* data;
ulong offset;
ulong sz_size_t;
bool error;
}
scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t);
if (t.ty == Tclass)
pbv.visitClass(cast(TypeClass)t);
else
t.accept(pbv);
return pbv.error ? ulong.max : sz;
}
/**
* get an array of size_t values that indicate possible pointer words in memory
* if interpreted as the type given as argument
* the first array element is the size of the type for independent interpretation
* of the array
* following elements bits represent one word (4/8 bytes depending on the target
* architecture). If set the corresponding memory might contain a pointer/reference.
*
* Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
*/
private Expression pointerBitmap(TraitsExp e)
{
if (!e.args || e.args.dim != 1)
{
error(e.loc, "a single type expected for trait pointerBitmap");
return ErrorExp.get();
}
Type t = getType((*e.args)[0]);
if (!t)
{
error(e.loc, "`%s` is not a type", (*e.args)[0].toChars());
return ErrorExp.get();
}
Array!(ulong) data;
ulong sz = getTypePointerBitmap(e.loc, t, &data);
if (sz == ulong.max)
return ErrorExp.get();
auto exps = new Expressions(data.dim + 1);
(*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t);
foreach (size_t i; 1 .. exps.dim)
(*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t);
auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.dim + 1), exps);
return ale;
}
Expression semanticTraits(TraitsExp e, Scope* sc)
{
static if (LOGSEMANTIC)
{
printf("TraitsExp::semantic() %s\n", e.toChars());
}
if (e.ident != Id.compiles &&
e.ident != Id.isSame &&
e.ident != Id.identifier &&
e.ident != Id.getProtection && e.ident != Id.getVisibility &&
e.ident != Id.getAttributes)
{
// Pretend we're in a deprecated scope so that deprecation messages
// aren't triggered when checking if a symbol is deprecated
const save = sc.stc;
if (e.ident == Id.isDeprecated)
sc.stc |= STC.deprecated_;
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
{
sc.stc = save;
return ErrorExp.get();
}
sc.stc = save;
}
size_t dim = e.args ? e.args.dim : 0;
Expression dimError(int expected)
{
e.error("expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim);
return ErrorExp.get();
}
static IntegerExp True()
{
return IntegerExp.createBool(true);
}
static IntegerExp False()
{
return IntegerExp.createBool(false);
}
/********
* Gets the function type from a given AST node
* if the node is a function of some sort.
* Params:
* o = an AST node to check for a `TypeFunction`
* fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null`
* Returns:
* a type node if `o` is a declaration of
* a delegate, function, function-pointer or a variable of the former.
* Otherwise, `null`.
*/
static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp)
{
Type t;
if (auto s = getDsymbolWithoutExpCtx(o))
{
if (auto fd = s.isFuncDeclaration())
{
t = fd.type;
fdp = fd;
}
else if (auto vd = s.isVarDeclaration())
t = vd.type;
else
t = isType(o);
}
else
t = isType(o);
if (t)
{
if (auto tf = t.isFunction_Delegate_PtrToFunction())
return tf;
}
return null;
}
IntegerExp isX(T)(bool delegate(T) fp)
{
if (!dim)
return False();
foreach (o; *e.args)
{
static if (is(T == Type))
auto y = getType(o);
static if (is(T : Dsymbol))
{
auto s = getDsymbolWithoutExpCtx(o);
if (!s)
return False();
}
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))
return False();
}
return True();
}
alias isTypeX = isX!Type;
alias isDsymX = isX!Dsymbol;
alias isDeclX = isX!Declaration;
alias isFuncX = isX!FuncDeclaration;
Expression isPkgX(bool function(Package) fp)
{
return isDsymX((Dsymbol sym) {
Package p = resolveIsPackage(sym);
return (p !is null) && fp(p);
});
}
if (e.ident == Id.isArithmetic)
{
return isTypeX(t => t.isintegral() || t.isfloating());
}
if (e.ident == Id.isFloating)
{
return isTypeX(t => t.isfloating());
}
if (e.ident == Id.isIntegral)
{
return isTypeX(t => t.isintegral());
}
if (e.ident == Id.isScalar)
{
return isTypeX(t => t.isscalar());
}
if (e.ident == Id.isUnsigned)
{
return isTypeX(t => t.isunsigned());
}
if (e.ident == Id.isAssociativeArray)
{
return isTypeX(t => t.toBasetype().ty == Taarray);
}
if (e.ident == Id.isDeprecated)
{
if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true))
return True();
return isDsymX(t => t.isDeprecated());
}
if (e.ident == Id.isFuture)
{
return isDeclX(t => t.isFuture());
}
if (e.ident == Id.isStaticArray)
{
return isTypeX(t => t.toBasetype().ty == Tsarray);
}
if (e.ident == Id.isAbstractClass)
{
return isTypeX(t => t.toBasetype().ty == Tclass &&
(cast(TypeClass)t.toBasetype()).sym.isAbstract());
}
if (e.ident == Id.isFinalClass)
{
return isTypeX(t => t.toBasetype().ty == Tclass &&
((cast(TypeClass)t.toBasetype()).sym.storage_class & STC.final_) != 0);
}
if (e.ident == Id.isTemplate)
{
if (dim != 1)
return dimError(1);
return isDsymX((s)
{
if (!s.toAlias().isOverloadable())
return false;
return overloadApply(s,
sm => sm.isTemplateDeclaration() !is null) != 0;
});
}
if (e.ident == Id.isPOD)
{
if (dim != 1)
return dimError(1);
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());
return ErrorExp.get();
}
Type tb = t.baseElemOf();
if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
{
return sd.isPOD() ? True() : False();
}
return True();
}
if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
{
if (dim != 1)
return dimError(1);
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());
return ErrorExp.get();
}
Type tb = t.baseElemOf();
if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
{
return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
: (sd.hasCopyCtor ? True() : False());
}
return False();
}
if (e.ident == Id.isCopyable)
{
if (dim != 1)
return dimError(1);
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());
return ErrorExp.get();
}
t = t.toBasetype(); // get the base in case `t` is an `enum`
if (auto ts = t.isTypeStruct())
{
ts.sym.dsymbolSemantic(sc);
}
return isCopyable(t) ? True() : False();
}
if (e.ident == Id.isNested)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto s = getDsymbolWithoutExpCtx(o);
if (!s)
{
}
else if (auto ad = s.isAggregateDeclaration())
{
return ad.isNested() ? True() : False();
}
else if (auto fd = s.isFuncDeclaration())
{
return fd.isNested() ? True() : False();
}
e.error("aggregate or function expected instead of `%s`", o.toChars());
return ErrorExp.get();
}
if (e.ident == Id.isDisabled)
{
if (dim != 1)
return dimError(1);
return isDeclX(f => f.isDisabled());
}
if (e.ident == Id.isAbstractFunction)
{
if (dim != 1)
return dimError(1);
return isFuncX(f => f.isAbstract());
}
if (e.ident == Id.isVirtualFunction)
{
if (dim != 1)
return dimError(1);
return isFuncX(f => f.isVirtual());
}
if (e.ident == Id.isVirtualMethod)
{
if (dim != 1)
return dimError(1);
return isFuncX(f => f.isVirtualMethod());
}
if (e.ident == Id.isFinalFunction)
{
if (dim != 1)
return dimError(1);
return isFuncX(f => f.isFinalFunc());
}
if (e.ident == Id.isOverrideFunction)
{
if (dim != 1)
return dimError(1);
return isFuncX(f => f.isOverride());
}
if (e.ident == Id.isStaticFunction)
{
if (dim != 1)
return dimError(1);
return isFuncX(f => !f.needThis() && !f.isNested());
}
if (e.ident == Id.isModule)
{
if (dim != 1)
return dimError(1);
return isPkgX(p => p.isModule() || p.isPackageMod());
}
if (e.ident == Id.isPackage)
{
if (dim != 1)
return dimError(1);
return isPkgX(p => p.isModule() is null);
}
if (e.ident == Id.isRef)
{
if (dim != 1)
return dimError(1);
return isDeclX(d => d.isRef());
}
if (e.ident == Id.isOut)
{
if (dim != 1)
return dimError(1);
return isDeclX(d => d.isOut());
}
if (e.ident == Id.isLazy)
{
if (dim != 1)
return dimError(1);
return isDeclX(d => (d.storage_class & STC.lazy_) != 0);
}
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
* a symbol should not be folded to a constant.
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
*/
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
return ErrorExp.get();
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
Identifier id;
if (auto po = isParameter(o))
{
if (!po.ident)
{
e.error("argument `%s` has no identifier", po.type.toChars());
return ErrorExp.get();
}
id = po.ident;
}
else
{
Dsymbol s = getDsymbolWithoutExpCtx(o);
if (!s || !s.ident)
{
e.error("argument `%s` has no identifier", o.toChars());
return ErrorExp.get();
}
id = s.ident;
}
auto se = new StringExp(e.loc, id.toString());
return se.expressionSemantic(sc);
}
if (e.ident == Id.getProtection || e.ident == Id.getVisibility)
{
if (dim != 1)
return dimError(1);
Scope* sc2 = sc.push();
sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
sc2.pop();
if (!ok)
return ErrorExp.get();
auto o = (*e.args)[0];
auto s = getDsymbolWithoutExpCtx(o);
if (!s)
{
if (!isError(o))
e.error("argument `%s` has no visibility", o.toChars());
return ErrorExp.get();
}
if (s.semanticRun == PASS.initial)
s.dsymbolSemantic(null);
auto protName = visibilityToString(s.visible().kind); // TODO: How about package(names)
assert(protName);
auto se = new StringExp(e.loc, protName);
return se.expressionSemantic(sc);
}
if (e.ident == Id.parent)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto s = getDsymbolWithoutExpCtx(o);
if (s)
{
// https://issues.dlang.org/show_bug.cgi?id=12496
// Consider:
// class T1
// {
// class C(uint value) { }
// }
// __traits(parent, T1.C!2)
if (auto ad = s.isAggregateDeclaration()) // `s` is `C`
{
if (ad.isNested()) // `C` is nested
{
if (auto p = s.toParent()) // `C`'s parent is `C!2`, believe it or not
{
if (p.isTemplateInstance()) // `C!2` is a template instance
{
s = p; // `C!2`'s parent is `T1`
auto td = (cast(TemplateInstance)p).tempdecl;
if (td)
s = td; // get the declaration context just in case there's two contexts
}
}
}
}
if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943
s = fd.toAliasFunc();
if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922
s = s.toParent();
}
if (!s || s.isImport())
{
e.error("argument `%s` has no parent", o.toChars());
return ErrorExp.get();
}
if (auto f = s.isFuncDeclaration())
{
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.expressionSemantic(sc);
return ex;
}
if (auto fld = f.isFuncLiteralDeclaration())
{
// Directly translate to VarExp instead of FuncExp
Expression ex = new VarExp(e.loc, fld, true);
return ex.expressionSemantic(sc);
}
}
return symbolToExp(s, e.loc, sc, false);
}
if (e.ident == Id.child)
{
if (dim != 2)
return dimError(2);
Expression ex;
auto op = (*e.args)[0];
if (auto symp = getDsymbol(op))
ex = new DsymbolExp(e.loc, symp);
else if (auto exp = op.isExpression())
ex = exp;
else
{
e.error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op.toChars());
return ErrorExp.get();
}
ex = ex.expressionSemantic(sc);
auto oc = (*e.args)[1];
auto symc = getDsymbol(oc);
if (!symc)
{
e.error("symbol expected as second argument of __traits `child` instead of `%s`", oc.toChars());
return ErrorExp.get();
}
if (auto d = symc.isDeclaration())
ex = new DotVarExp(e.loc, ex, d);
else if (auto td = symc.isTemplateDeclaration())
ex = new DotExp(e.loc, ex, new TemplateExp(e.loc, td));
else if (auto ti = symc.isScopeDsymbol())
ex = new DotExp(e.loc, ex, new ScopeExp(e.loc, ti));
else
assert(0);
ex = ex.expressionSemantic(sc);
return ex;
}
if (e.ident == Id.toType)
{
if (dim != 1)
return dimError(1);
auto ex = isExpression((*e.args)[0]);
if (!ex)
{
e.error("expression expected as second argument of __traits `%s`", e.ident.toChars());
return ErrorExp.get();
}
ex = ex.ctfeInterpret();
StringExp se = semanticString(sc, ex, "__traits(toType, string)");
if (!se)
{
return ErrorExp.get();
}
Type t = decoToType(se.toUTF8(sc).peekString());
if (!t)
{
e.error("cannot determine `%s`", e.toChars());
return ErrorExp.get();
}
return (new TypeExp(e.loc, t)).expressionSemantic(sc);
}
if (e.ident == Id.hasMember ||
e.ident == Id.getMember ||
e.ident == Id.getOverloads ||
e.ident == Id.getVirtualMethods ||
e.ident == Id.getVirtualFunctions)
{
if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads))
return dimError(2);
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 ErrorExp.get();
}
ex = ex.ctfeInterpret();
bool includeTemplates = false;
if (dim == 3 && e.ident == Id.getOverloads)
{
auto b = isExpression((*e.args)[2]);
b = b.ctfeInterpret();
if (!b.type.equals(Type.tbool))
{
e.error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars());
return ErrorExp.get();
}
includeTemplates = b.toBool().get();
}
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 ErrorExp.get();
}
se = se.toUTF8(sc);
if (se.sz != 1)
{
e.error("string must be chars");
return ErrorExp.get();
}
auto id = Identifier.idPool(se.peekString());
/* Prefer a Type, because getDsymbol(Type) can lose type modifiers.
Then a Dsymbol, because it might need some runtime contexts.
*/
Dsymbol sym = getDsymbol(o);
if (auto t = isType(o))
ex = typeDotIdExp(e.loc, t, id);
else if (sym)
{
if (e.ident == Id.hasMember)
{
if (auto sm = sym.search(e.loc, id))
return True();
}
ex = new DsymbolExp(e.loc, sym);
ex = new DotIdExp(e.loc, ex, id);
}
else if (auto ex2 = isExpression(o))
ex = new DotIdExp(e.loc, ex2, id);
else
{
e.error("invalid first argument");
return ErrorExp.get();
}
// ignore symbol visibility and disable access checks for these traits
Scope* scx = sc.push();
scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck;
scope (exit) scx.pop();
if (e.ident == Id.hasMember)
{
/* Take any errors as meaning it wasn't found
*/
ex = ex.trySemantic(scx);
return ex ? True() : False();
}
else if (e.ident == Id.getMember)
{
if (auto die = ex.isDotIdExp())
// Prevent semantic() from replacing Symbol with its initializer
die.wantsym = true;
ex = ex.expressionSemantic(scx);
return ex;
}
else if (e.ident == Id.getVirtualFunctions ||
e.ident == Id.getVirtualMethods ||
e.ident == Id.getOverloads)
{
uint errors = global.errors;
Expression eorig = ex;
ex = ex.expressionSemantic(scx);
if (errors < global.errors)
e.error("`%s` cannot be resolved", eorig.toChars());
/* Create tuple of functions of ex
*/
auto exps = new Expressions();
Dsymbol f;
if (auto ve = ex.isVarExp)
{
if (ve.var.isFuncDeclaration() || ve.var.isOverDeclaration())
f = ve.var;
ex = null;
}
else if (auto dve = ex.isDotVarExp)
{
if (dve.var.isFuncDeclaration() || dve.var.isOverDeclaration())
f = dve.var;
if (dve.e1.op == EXP.dotType || dve.e1.op == EXP.this_)
ex = null;
else
ex = dve.e1;
}
else if (auto te = ex.isTemplateExp)
{
auto td = te.td;
f = td;
if (td && td.funcroot)
f = td.funcroot;
ex = null;
}
else if (auto dte = ex.isDotTemplateExp)
{
auto td = dte.td;
f = td;
if (td && td.funcroot)
f = td.funcroot;
ex = null;
if (dte.e1.op != EXP.dotType && dte.e1.op != EXP.this_)
ex = dte.e1;
}
bool[string] funcTypeHash;
/* Compute the function signature and insert it in the
* hashtable, if not present. This is needed so that
* traits(getOverlods, F3, "visit") does not count `int visit(int)`
* twice in the following example:
*
* =============================================
* interface F1 { int visit(int);}
* interface F2 { int visit(int); void visit(); }
* interface F3 : F2, F1 {}
*==============================================
*/
void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e)
{
auto signature = fd.type.toString();
//printf("%s - %s\n", fd.toChars, signature);
if (signature !in funcTypeHash)
{
funcTypeHash[signature] = true;
exps.push(e);
}
}
int dg(Dsymbol s)
{
auto fd = s.isFuncDeclaration();
if (!fd)
{
if (includeTemplates)
{
if (auto td = s.isTemplateDeclaration())
{
// if td is part of an overload set we must take a copy
// which shares the same `instances` cache but without
// `overroot` and `overnext` set to avoid overload
// behaviour in the result.
if (td.overnext !is null)
{
if (td.instances is null)
{
// create an empty AA just to copy it
scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
auto tib = TemplateInstanceBox(ti);
td.instances[tib] = null;
td.instances.clear();
}
td = td.syntaxCopy(null);
import core.stdc.string : memcpy;
memcpy(cast(void*) td, cast(void*) s,
__traits(classInstanceSize, TemplateDeclaration));
td.overroot = null;
td.overnext = null;
}
auto e = ex ? new DotTemplateExp(Loc.initial, ex, td)
: new DsymbolExp(Loc.initial, td);
exps.push(e);
}
}
return 0;
}
if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
return 0;
if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
return 0;
auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
fa.visibility = fd.visibility;
auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false)
: new DsymbolExp(Loc.initial, fa, false);
// if the parent is an interface declaration
// we must check for functions with the same signature
// in different inherited interfaces
if (sym && sym.isInterfaceDeclaration())
insertInterfaceInheritedFunction(fd, e);
else
exps.push(e);
return 0;
}
InterfaceDeclaration ifd = null;
if (sym)
ifd = sym.isInterfaceDeclaration();
// If the symbol passed as a parameter is an
// interface that inherits other interfaces
overloadApply(f, &dg);
if (ifd && ifd.interfaces && f)
{
// check the overloads of each inherited interface individually
foreach (bc; ifd.interfaces)
{
if (auto fd = bc.sym.search(e.loc, f.ident))
overloadApply(fd, &dg);
}
}
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(scx);
}
else
assert(0);
}
if (e.ident == Id.classInstanceSize)
{
if (dim != 1)
return dimError(1);
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 ErrorExp.get();
}
if (cd.sizeok != Sizeok.done)
{
cd.size(e.loc);
}
if (cd.sizeok != Sizeok.done)
{
e.error("%s `%s` is forward referenced", cd.kind(), cd.toChars());
return ErrorExp.get();
}
return new IntegerExp(e.loc, cd.structsize, Type.tsize_t);
}
if (e.ident == Id.getAliasThis)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto s = getDsymbol(o);
auto ad = s ? s.isAggregateDeclaration() : null;
auto exps = new Expressions();
if (ad && ad.aliasthis)
exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString()));
Expression ex = new TupleExp(e.loc, exps);
ex = ex.expressionSemantic(sc);
return ex;
}
if (e.ident == Id.getAttributes)
{
/* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
* a symbol should not be folded to a constant.
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
*/
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
return ErrorExp.get();
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto po = isParameter(o);
auto s = getDsymbolWithoutExpCtx(o);
UserAttributeDeclaration udad = null;
if (po)
{
udad = po.userAttribDecl;
}
else if (s)
{
if (s.isImport())
{
s = s.isImport().mod;
}
//printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
udad = s.userAttribDecl;
}
else
{
version (none)
{
Expression x = isExpression(o);
Type t = isType(o);
if (x)
printf("e = %s %s\n", EXPtoString(x.op).ptr, x.toChars());
if (t)
printf("t = %d %s\n", t.ty, t.toChars());
}
e.error("first argument is not a symbol");
return ErrorExp.get();
}
auto exps = udad ? udad.getAttributes() : new Expressions();
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(sc);
}
if (e.ident == Id.getFunctionAttributes)
{
/* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
* https://dlang.org/spec/traits.html#getFunctionAttributes
*/
if (dim != 1)
return dimError(1);
FuncDeclaration fd;
TypeFunction tf = toTypeFunction((*e.args)[0], fd);
if (!tf)
{
e.error("first argument is not a function");
return ErrorExp.get();
}
auto mods = new Expressions();
void addToMods(string str)
{
mods.push(new StringExp(Loc.initial, str));
}
tf.modifiersApply(&addToMods);
tf.attributesApply(&addToMods, TRUSTformatSystem);
auto tup = new TupleExp(e.loc, mods);
return tup.expressionSemantic(sc);
}
if (e.ident == Id.isReturnOnStack)
{
/* Extract as a boolean if function return value is on the stack
* https://dlang.org/spec/traits.html#isReturnOnStack
*/
if (dim != 1)
return dimError(1);
RootObject o = (*e.args)[0];
FuncDeclaration fd;
TypeFunction tf = toTypeFunction(o, fd);
if (!tf)
{
e.error("argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars());
return ErrorExp.get();
}
bool value = target.isReturnOnStack(tf, fd && fd.needThis());
return IntegerExp.createBool(value);
}
if (e.ident == Id.getFunctionVariadicStyle)
{
/* Accept a symbol or a type. Returns one of the following:
* "none" not a variadic function
* "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments`
* "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg
* "typesafe" void typesafe(T[] ...)
*/
// get symbol linkage as a string
if (dim != 1)
return dimError(1);
LINK link;
VarArg varargs;
auto o = (*e.args)[0];
FuncDeclaration fd;
TypeFunction tf = toTypeFunction(o, fd);
if (tf)
{
link = tf.linkage;
varargs = tf.parameterList.varargs;
}
else
{
if (!fd)
{
e.error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars());
return ErrorExp.get();
}
link = fd.linkage;
varargs = fd.getParameterList().varargs;
}
string style;
final switch (varargs)
{
case VarArg.none: style = "none"; break;
case VarArg.variadic: style = (link == LINK.d)
? "argptr"
: "stdarg"; break;
case VarArg.typesafe: style = "typesafe"; break;
}
auto se = new StringExp(e.loc, style);
return se.expressionSemantic(sc);
}
if (e.ident == Id.getParameterStorageClasses)
{
/* Accept a function symbol or a type, followed by a parameter index.
* Returns a tuple of strings of the parameter's storage classes.
*/
// get symbol linkage as a string
if (dim != 2)
return dimError(2);
auto o = (*e.args)[0];
auto o1 = (*e.args)[1];
ParameterList fparams;
CallExp ce;
if (auto exp = isExpression(o))
ce = exp.isCallExp();
if (ce)
{
fparams = ce.f.getParameterList();
}
else
{
FuncDeclaration fd;
auto tf = toTypeFunction(o, fd);
if (tf)
fparams = tf.parameterList;
else if (fd)
fparams = fd.getParameterList();
else
{
e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function or a function call",
o.toChars(), o1.toChars());
return ErrorExp.get();
}
}
// Avoid further analysis for invalid functions leading to misleading error messages
if (!fparams.parameters)
return ErrorExp.get();
StorageClass stc;
// Set stc to storage class of the ith parameter
auto ex = isExpression((*e.args)[1]);
if (!ex)
{
e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
o.toChars(), o1.toChars());
return ErrorExp.get();
}
ex = ex.ctfeInterpret();
auto ii = ex.toUInteger();
if (ii >= fparams.length)
{
e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars());
return ErrorExp.get();
}
uint n = cast(uint)ii;
Parameter p = fparams[n];
stc = p.storageClass;
// This mirrors hdrgen.visit(Parameter p)
if (p.type && p.type.mod & MODFlags.shared_)
stc &= ~STC.shared_;
auto exps = new Expressions;
void push(string s)
{
exps.push(new StringExp(e.loc, s));
}
if (stc & STC.auto_)
push("auto");
if (stc & STC.return_)
push("return");
if (stc & STC.out_)
push("out");
else if (stc & STC.in_)
push("in");
else if (stc & STC.ref_)
push("ref");
else if (stc & STC.lazy_)
push("lazy");
else if (stc & STC.alias_)
push("alias");
if (stc & STC.const_)
push("const");
if (stc & STC.immutable_)
push("immutable");
if (stc & STC.wild)
push("inout");
if (stc & STC.shared_)
push("shared");
if (stc & STC.scope_ && !(stc & STC.scopeinferred))
push("scope");
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(sc);
}
if (e.ident == Id.getLinkage)
{
// get symbol linkage as a string
if (dim != 1)
return dimError(1);
LINK link;
auto o = (*e.args)[0];
FuncDeclaration fd;
TypeFunction tf = toTypeFunction(o, fd);
if (tf)
link = tf.linkage;
else
{
auto s = getDsymbol(o);
Declaration d;
AggregateDeclaration agg;
if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null))
{
e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars());
return ErrorExp.get();
}
if (d !is null)
link = d.linkage;
else
{
// Resolves forward references
if (agg.sizeok != Sizeok.done)
{
agg.size(e.loc);
if (agg.sizeok != Sizeok.done)
{
e.error("%s `%s` is forward referenced", agg.kind(), agg.toChars());
return ErrorExp.get();
}
}
final switch (agg.classKind)
{
case ClassKind.d:
link = LINK.d;
break;
case ClassKind.cpp:
link = LINK.cpp;
break;
case ClassKind.objc:
link = LINK.objc;
break;
case ClassKind.c:
link = LINK.c;
break;
}
}
}
auto linkage = linkageToChars(link);
auto se = new StringExp(e.loc, linkage.toDString());
return se.expressionSemantic(sc);
}
if (e.ident == Id.allMembers ||
e.ident == Id.derivedMembers)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto s = getDsymbol(o);
if (!s)
{
e.error("In expression `%s` `%s` can't have members", e.toChars(), o.toChars());
e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars());
return ErrorExp.get();
}
if (auto imp = s.isImport())
{
// https://issues.dlang.org/show_bug.cgi?id=9692
s = imp.mod;
}
// https://issues.dlang.org/show_bug.cgi?id=16044
if (auto p = s.isPackage())
{
if (auto pm = p.isPackageMod())
s = pm;
}
auto sds = s.isScopeDsymbol();
if (!sds || sds.isTemplateDeclaration())
{
e.error("In expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars());
e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars());
return ErrorExp.get();
}
auto idents = new Identifiers();
int pushIdentsDg(size_t n, Dsymbol sm)
{
if (!sm)
return 1;
// skip local symbols, such as static foreach loop variables
if (auto decl = sm.isDeclaration())
{
if (decl.storage_class & STC.local)
{
return 0;
}
// skip 'this' context pointers
else if (decl.isThisDeclaration())
return 0;
}
// https://issues.dlang.org/show_bug.cgi?id=20915
// skip version and debug identifiers
if (sm.isVersionSymbol() || sm.isDebugSymbol())
return 0;
//printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
if (sm.ident)
{
// https://issues.dlang.org/show_bug.cgi?id=10096
// https://issues.dlang.org/show_bug.cgi?id=10100
// Skip over internal members in __traits(allMembers)
if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) ||
(sm.isDtorDeclaration() && sm.ident != Id.dtor) ||
(sm.isPostBlitDeclaration() && sm.ident != Id.postblit) ||
sm.isInvariantDeclaration() ||
sm.isUnitTestDeclaration())
{
return 0;
}
if (sm.ident == Id.empty)
{
return 0;
}
if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
return 0;
if ((!sds.isModule() && !sds.isPackage()) && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
return 0;
//printf("\t%s\n", sm.ident.toChars());
/* Skip if already present in idents[]
*/
foreach (id; *idents)
{
if (id == sm.ident)
return 0;
// Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
debug
{
import core.stdc.string : strcmp;
assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
}
}
idents.push(sm.ident);
}
else if (auto ed = sm.isEnumDeclaration())
{
ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
}
return 0;
}
ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
auto cd = sds.isClassDeclaration();
if (cd && e.ident == Id.allMembers)
{
if (cd.semanticRun < PASS.semanticdone)
cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668
// Try to resolve forward reference
void pushBaseMembersDg(ClassDeclaration cd)
{
for (size_t i = 0; i < cd.baseclasses.dim; i++)
{
auto cb = (*cd.baseclasses)[i].sym;
assert(cb);
ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
if (cb.baseclasses.dim)
pushBaseMembersDg(cb);
}
}
pushBaseMembersDg(cd);
}
// Turn Identifiers into StringExps reusing the allocated array
assert(Expressions.sizeof == Identifiers.sizeof);
auto exps = cast(Expressions*)idents;
foreach (i, id; *idents)
{
auto se = new StringExp(e.loc, id.toString());
(*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, ...) ]
*/
Expression ex = new TupleExp(e.loc, exps);
ex = ex.expressionSemantic(sc);
return ex;
}
if (e.ident == Id.compiles)
{
/* Determine if all the objects - types, expressions, or symbols -
* compile without error
*/
if (!dim)
return False();
foreach (o; *e.args)
{
uint errors = global.startGagging();
Scope* sc2 = sc.push();
sc2.tinst = null;
sc2.minst = null;
sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
bool err = false;
auto t = isType(o);
while (t)
{
if (auto tm = t.isTypeMixin())
{
/* The mixin string could be a type or an expression.
* Have to try compiling it to see.
*/
OutBuffer buf;
if (expressionsToString(buf, sc, tm.exps))
{
err = true;
break;
}
const olderrors = global.errors;
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false);
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);
o = p.parseTypeOrAssignExp(TOK.endOfFile);
if (olderrors != global.errors || p.token.value != TOK.endOfFile)
{
err = true;
break;
}
t = o.isType();
}
else
break;
}
if (!err)
{
auto ex = t ? t.typeToExpression() : isExpression(o);
if (!ex && t)
{
Dsymbol s;
t.resolve(e.loc, sc2, ex, t, s);
if (t)
{
t.typeSemantic(e.loc, sc2);
if (t.ty == Terror)
err = true;
}
else if (s && s.errors)
err = true;
}
if (ex)
{
ex = ex.expressionSemantic(sc2);
ex = resolvePropertiesOnly(sc2, ex);
ex = ex.optimize(WANTvalue);
if (sc2.func && sc2.func.type.ty == Tfunction)
{
const tf = cast(TypeFunction)sc2.func.type;
err |= tf.isnothrow && canThrow(ex, sc2.func, false);
}
ex = checkGC(sc2, ex);
if (ex.op == EXP.error)
err = true;
}
}
// Carefully detach the scope from the parent and throw it away as
// we only need it to evaluate the expression
// https://issues.dlang.org/show_bug.cgi?id=15428
sc2.detach();
if (global.endGagging(errors) || err)
{
return False();
}
}
return True();
}
if (e.ident == Id.isSame)
{
/* Determine if two symbols are the same
*/
if (dim != 2)
return dimError(2);
// https://issues.dlang.org/show_bug.cgi?id=20761
// tiarg semantic may expand in place the list of arguments, for example:
//
// before tiarg sema: __traits(isSame, seq!(0,0), seq!(1,1))
// after : __traits(isSame, 0, 0, 1, 1)
//
// so we split in two lists
Objects ob1;
ob1.push((*e.args)[0]);
Objects ob2;
ob2.push((*e.args)[1]);
if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
return ErrorExp.get();
if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
return ErrorExp.get();
if (ob1.dim != ob2.dim)
return False();
foreach (immutable i; 0 .. ob1.dim)
if (!ob1[i].isSame(ob2[i], sc))
return False();
return True();
}
if (e.ident == Id.getUnitTests)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto s = getDsymbolWithoutExpCtx(o);
if (!s)
{
e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate",
o.toChars());
return ErrorExp.get();
}
if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
s = imp.mod;
auto sds = s.isScopeDsymbol();
if (!sds || sds.isTemplateDeclaration())
{
e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s",
s.toChars(), s.kind());
return ErrorExp.get();
}
auto exps = new Expressions();
if (global.params.useUnitTests)
{
bool[void*] uniqueUnitTests;
void symbolDg(Dsymbol s)
{
if (auto ad = s.isAttribDeclaration())
{
ad.include(null).foreachDsymbol(&symbolDg);
}
else if (auto tm = s.isTemplateMixin())
{
tm.members.foreachDsymbol(&symbolDg);
}
else if (auto ud = s.isUnitTestDeclaration())
{
if (cast(void*)ud in uniqueUnitTests)
return;
uniqueUnitTests[cast(void*)ud] = true;
auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
ad.visibility = ud.visibility;
auto e = new DsymbolExp(Loc.initial, ad, false);
exps.push(e);
}
}
sds.members.foreachDsymbol(&symbolDg);
}
auto te = new TupleExp(e.loc, exps);
return te.expressionSemantic(sc);
}
if (e.ident == Id.getVirtualIndex)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
auto s = getDsymbolWithoutExpCtx(o);
auto fd = s ? s.isFuncDeclaration() : null;
if (!fd)
{
e.error("first argument to __traits(getVirtualIndex) must be a function");
return ErrorExp.get();
}
fd = fd.toAliasFunc(); // Necessary to support multiple overloads.
return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
}
if (e.ident == Id.getPointerBitmap)
{
return pointerBitmap(e);
}
if (e.ident == Id.initSymbol)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
Type t = isType(o);
AggregateDeclaration ad = t ? isAggregate(t) : null;
// Interfaces don't have an init symbol and hence cause linker errors
if (!ad || ad.isInterfaceDeclaration())
{
e.error("struct / class type expected as argument to __traits(initSymbol) instead of `%s`", o.toChars());
return ErrorExp.get();
}
Declaration d = new SymbolDeclaration(ad.loc, ad);
d.type = Type.tvoid.arrayOf().constOf();
d.storage_class |= STC.rvalue;
return new VarExp(e.loc, d);
}
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 ErrorExp.get();
}
Type tb = t.baseElemOf();
return tb.isZeroInit(e.loc) ? True() : False();
}
if (e.ident == Id.getTargetInfo)
{
if (dim != 1)
return dimError(1);
auto ex = isExpression((*e.args)[0]);
StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null;
if (!ex || !se || se.len == 0)
{
e.error("string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
return ErrorExp.get();
}
se = se.toUTF8(sc);
const slice = se.peekString();
Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0
if (!r)
{
e.error("`getTargetInfo` key `\"%.*s\"` not supported by this implementation",
cast(int)slice.length, slice.ptr);
return ErrorExp.get();
}
return r.expressionSemantic(sc);
}
if (e.ident == Id.getLocation)
{
if (dim != 1)
return dimError(1);
auto arg0 = (*e.args)[0];
Dsymbol s = getDsymbolWithoutExpCtx(arg0);
if (!s || !s.loc.isValid())
{
e.error("can only get the location of a symbol, not `%s`", arg0.toChars());
return ErrorExp.get();
}
const fd = s.isFuncDeclaration();
// FIXME:td.overnext is always set, even when using an index on it
//const td = s.isTemplateDeclaration();
if ((fd && fd.overnext) /*|| (td && td.overnext)*/)
{
e.error("cannot get location of an overload set, " ~
"use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~
"to get the Nth overload",
arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr);
return ErrorExp.get();
}
auto exps = new Expressions(3);
(*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString());
(*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32);
(*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32);
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(sc);
}
if (e.ident == Id.getCppNamespaces)
{
auto o = (*e.args)[0];
auto s = getDsymbolWithoutExpCtx(o);
auto exps = new Expressions(0);
if (auto d = s.isDeclaration())
{
if (d.inuse)
{
d.error("circular reference in `__traits(GetCppNamespaces,...)`");
return ErrorExp.get();
}
d.inuse = 1;
}
/**
Prepend the namespaces in the linked list `ns` to `es`.
Returns: true if `ns` contains an `ErrorExp`.
*/
bool prependNamespaces(Expressions* es, CPPNamespaceDeclaration ns)
{
// Semantic processing will convert `extern(C++, "a", "b", "c")`
// into `extern(C++, "a") extern(C++, "b") extern(C++, "c")`,
// creating a linked list what `a`'s `cppnamespace` points to `b`,
// and `b`'s points to `c`. Our entry point is `a`.
for (; ns !is null; ns = ns.cppnamespace)
{
ns.dsymbolSemantic(sc);
if (ns.exp.isErrorExp())
return true;
auto se = ns.exp.toStringExp();
// extern(C++, (emptyTuple))
// struct D {}
// will produce a blank ident
if (!se.len)
continue;
es.insert(0, se);
}
return false;
}
for (auto p = s; !p.isModule(); p = p.toParent())
{
p.dsymbolSemantic(sc);
auto pp = p.toParent();
if (pp.isTemplateInstance())
{
if (!p.cppnamespace)
continue;
//if (!p.toParent().cppnamespace)
// continue;
auto inner = new Expressions(0);
auto outer = new Expressions(0);
if (prependNamespaces(inner, p.cppnamespace)) return ErrorExp.get();
if (prependNamespaces(outer, pp.cppnamespace)) return ErrorExp.get();
size_t i = 0;
while(i < outer.dim && ((*inner)[i]) == (*outer)[i])
i++;
foreach_reverse (ns; (*inner)[][i .. $])
exps.insert(0, ns);
continue;
}
if (p.isNspace())
exps.insert(0, new StringExp(p.loc, p.ident.toString()));
if (prependNamespaces(exps, p.cppnamespace))
return ErrorExp.get();
}
if (auto d = s.isDeclaration())
d.inuse = 0;
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(sc);
}
//https://issues.dlang.org/show_bug.cgi?id=22291
if (e.ident == Id.parameters)
{
//No args are valid
if (e.args)
{
char[] contents = cast(char[]) e.args.toString();
contents = contents[1..$];
contents[$-1] = '\0';
e.error("`__traits(parameters)` cannot have arguments, but `%s` was supplied", contents.ptr);
return ErrorExp.get();
}
auto fd = sc.getEnclosingFunction();
if (!fd)
{
e.error("`__traits(parameters)` may only be used inside a function");
return ErrorExp.get();
}
auto tf = fd.type.isTypeFunction();
assert(tf);
auto exps = new Expressions(0);
int addParameterDG(size_t idx, Parameter x)
{
assert(x.ident);
exps.push(new IdentifierExp(e.loc, x.ident));
return 0;
}
/*
This is required since not all "parameters" actually have a name
until they (tuples) are expanded e.g. an anonymous tuple parameter's
contents get given names but not the tuple itself.
*/
Parameter._foreach(tf.parameterList.parameters, &addParameterDG);
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(sc);
}
static const(char)[] trait_search_fp(const(char)[] seed, out int cost)
{
//printf("trait_search_fp('%s')\n", seed);
if (!seed.length)
return null;
cost = 0; // all the same cost
const sv = traitsStringTable.lookup(seed);
return sv ? sv.toString() : null;
}
if (auto sub = speller!trait_search_fp(e.ident.toString()))
e.error("unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr);
else
e.error("unrecognized trait `%s`", e.ident.toChars());
return ErrorExp.get();
}
/// compare arguments of __traits(isSame)
private bool isSame(RootObject o1, RootObject o2, Scope* sc)
{
static FuncLiteralDeclaration isLambda(RootObject oarg)
{
if (auto t = isDsymbol(oarg))
{
if (auto td = t.isTemplateDeclaration())
{
if (td.members && td.members.dim == 1)
{
if (auto fd = (*td.members)[0].isFuncLiteralDeclaration())
return fd;
}
}
}
else if (auto ea = isExpression(oarg))
{
if (ea.op == EXP.function_)
{
if (auto fe = ea.isFuncExp())
return fe.fd;
}
}
return null;
}
auto l1 = isLambda(o1);
auto l2 = isLambda(o2);
if (l1 && l2)
{
import dmd.lambdacomp : isSameFuncLiteral;
if (isSameFuncLiteral(l1, l2, sc))
return true;
}
// issue 12001, allow isSame, <BasicType>, <BasicType>
Type t1 = isType(o1);
Type t2 = isType(o2);
if (t1 && t2 && t1.equals(t2))
return true;
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)
{
if (auto ea = isExpression(o1))
printf("%s\n", ea.toChars());
if (auto ta = isType(o1))
printf("%s\n", ta.toChars());
return false;
}
else
printf("%s %s\n", s1.kind(), s1.toChars());
}
if (!s1 && !s2)
{
auto ea1 = isExpression(o1);
auto ea2 = isExpression(o2);
if (ea1 && ea2)
{
if (ea1.equals(ea2))
return true;
}
}
if (!s1 || !s2)
return false;
s1 = s1.toAlias();
s2 = s2.toAlias();
if (auto fa1 = s1.isFuncAliasDeclaration())
s1 = fa1.toAliasFunc();
if (auto fa2 = s2.isFuncAliasDeclaration())
s2 = fa2.toAliasFunc();
// https://issues.dlang.org/show_bug.cgi?id=11259
// compare import symbol to a package symbol
static bool cmp(Dsymbol s1, Dsymbol s2)
{
auto imp = s1.isImport();
return imp && imp.pkg && imp.pkg == s2.isPackage();
}
if (cmp(s1,s2) || cmp(s2,s1))
return true;
if (s1 == s2)
return true;
// https://issues.dlang.org/show_bug.cgi?id=18771
// OverloadSets are equal if they contain the same functions
auto overSet1 = s1.isOverloadSet();
if (!overSet1)
return false;
auto overSet2 = s2.isOverloadSet();
if (!overSet2)
return false;
if (overSet1.a.dim != overSet2.a.dim)
return false;
// OverloadSets contain array of Dsymbols => O(n*n)
// to compare for equality as the order of overloads
// might not be the same
Lnext:
foreach(overload1; overSet1.a)
{
foreach(overload2; overSet2.a)
{
if (overload1 == overload2)
continue Lnext;
}
return false;
}
return true;
}