aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2021-01-04 19:05:38 +0100
committerIain Buclaw <ibuclaw@gdcproject.org>2021-01-05 22:09:10 +0100
commitc5e94699efa816444c0ae49ad55d0e01a48203df (patch)
treef527fd1748c2f034d74ca1b268d4f56fc36cf050 /gcc/d/dmd
parentae1ada95fee1ee4fac0dec0486076d9787988a03 (diff)
downloadgcc-c5e94699efa816444c0ae49ad55d0e01a48203df.zip
gcc-c5e94699efa816444c0ae49ad55d0e01a48203df.tar.gz
gcc-c5e94699efa816444c0ae49ad55d0e01a48203df.tar.bz2
d: Merge upstream dmd a5c86f5b9
Adds the following new `__traits' to the D language. - isDeprecated: used to detect if a function is deprecated. - isDisabled: used to detect if a function is marked with @disable. - isFuture: used to detect if a function is marked with @__future. - isModule: used to detect if a given symbol represents a module, this enhancement also adds support using `is(sym == module)'. - isPackage: used to detect if a given symbol represents a package, this enhancement also adds support using `is(sym == package)'. - child: takes two arguments. The first must be a symbol or expression and the second must be a symbol, such as an alias to a member of the first 'parent' argument. The result is the second 'member' argument interpreted with its 'this' context set to 'parent'. This is the inverse of `__traits(parent, member)'. - isReturnOnStack: determines if a function's return value is placed on the stack, or is returned via registers. - isZeroInit: used to detect if a type's default initializer has no non-zero bits. - getTargetInfo: used to query features of the target being compiled for, the back-end can expand this to register any key to handle the given argument, however a reliable subset exists which includes "cppRuntimeLibrary", "cppStd", "floatAbi", and "objectFormat". - getLocation: returns a tuple whose entries correspond to the filename, line number, and column number of where the argument was declared. - hasPostblit: used to detect if a type is a struct with a postblit. - isCopyable: used to detect if a type allows copying its value. - getVisibility: an alias for the getProtection trait. Reviewed-on: https://github.com/dlang/dmd/pull/12093 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd a5c86f5b9. * d-builtins.cc (d_eval_constant_expression): Handle ADDR_EXPR trees created by build_string_literal. * d-frontend.cc (retStyle): Remove function. * d-target.cc (d_language_target_info): New variable. (d_target_info_table): Likewise. (Target::_init): Initialize d_target_info_table. (Target::isReturnOnStack): New function. (d_add_target_info_handlers): Likewise. (d_handle_target_cpp_std): Likewise. (d_handle_target_cpp_runtime_library): Likewise. (Target::getTargetInfo): Likewise. * d-target.h (struct d_target_info_spec): New type. (d_add_target_info_handlers): Declare.
Diffstat (limited to 'gcc/d/dmd')
-rw-r--r--gcc/d/dmd/MERGE2
-rw-r--r--gcc/d/dmd/declaration.h3
-rw-r--r--gcc/d/dmd/dmodule.c289
-rw-r--r--gcc/d/dmd/dstruct.c118
-rw-r--r--gcc/d/dmd/dtemplate.c6
-rw-r--r--gcc/d/dmd/expression.c9
-rw-r--r--gcc/d/dmd/expressionsem.c67
-rw-r--r--gcc/d/dmd/func.c39
-rw-r--r--gcc/d/dmd/globals.h2
-rw-r--r--gcc/d/dmd/idgen.c13
-rw-r--r--gcc/d/dmd/module.h2
-rw-r--r--gcc/d/dmd/mtype.c1
-rw-r--r--gcc/d/dmd/parse.c15
-rw-r--r--gcc/d/dmd/root/filename.c14
-rw-r--r--gcc/d/dmd/root/filename.h1
-rw-r--r--gcc/d/dmd/target.h3
-rw-r--r--gcc/d/dmd/traits.c684
17 files changed, 937 insertions, 331 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 1f695b9..1629b45 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-2bd4fc3fed8b8cd9760e77c6b2a1905cd84d0e70
+a5c86f5b92c4cd3afde910c89881ccaea11de554
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index 65ac3f7..a4e7766 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -149,6 +149,7 @@ public:
bool isSynchronized() { return (storage_class & STCsynchronized) != 0; }
bool isParameter() { return (storage_class & STCparameter) != 0; }
bool isDeprecated() { return (storage_class & STCdeprecated) != 0; }
+ bool isDisabled() { return (storage_class & STCdisable) != 0; }
bool isOverride() { return (storage_class & STCoverride) != 0; }
bool isResult() { return (storage_class & STCresult) != 0; }
bool isField() { return (storage_class & STCfield) != 0; }
@@ -669,7 +670,7 @@ public:
static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0);
static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0);
void checkDmain();
- bool checkNrvo();
+ bool checkNRVO();
FuncDeclaration *isFuncDeclaration() { return this; }
diff --git a/gcc/d/dmd/dmodule.c b/gcc/d/dmd/dmodule.c
index 8f09f2d..305b133 100644
--- a/gcc/d/dmd/dmodule.c
+++ b/gcc/d/dmd/dmodule.c
@@ -34,7 +34,6 @@ Dsymbols Module::deferred2; // deferred Dsymbol's needing semantic2() run on the
Dsymbols Module::deferred3; // deferred Dsymbol's needing semantic3() run on them
unsigned Module::dprogress;
-const char *lookForSourceFile(const char **path, const char *filename);
StringExp *semanticString(Scope *sc, Expression *exp, const char *s);
void Module::_init()
@@ -72,7 +71,6 @@ Module::Module(const char *filename, Identifier *ident, int doDocComment, int do
sfilename = NULL;
importedFrom = NULL;
srcfile = NULL;
- srcfilePath = NULL;
docfile = NULL;
debuglevel = 0;
@@ -109,9 +107,6 @@ Module::Module(const char *filename, Identifier *ident, int doDocComment, int do
fatal();
}
srcfile = new File(srcfilename);
- if (!FileName::absolute(srcfilename))
- srcfilePath = getcwd(NULL, 0);
-
objfile = setOutfile(global.params.objname.ptr, global.params.objdir.ptr, filename, global.obj_ext.ptr);
if (doDocComment)
@@ -215,57 +210,149 @@ static void checkModFileAlias(OutBuffer *buf, OutBuffer *dotmods,
dotmods->writeByte('.');
}
-Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident)
+/**
+ * Converts a chain of identifiers to the filename of the module
+ *
+ * Params:
+ * packages = the names of the "parent" packages
+ * ident = the name of the child package or module
+ *
+ * Returns:
+ * the filename of the child package or module
+ */
+static const char *getFilename(Identifiers *packages, Identifier *ident)
{
- //printf("Module::load(ident = '%s')\n", ident->toChars());
-
- // Build module filename by turning:
- // foo.bar.baz
- // into:
- // foo\bar\baz
const char *filename = ident->toChars();
- if (packages && packages->length)
- {
- OutBuffer buf;
- OutBuffer dotmods;
- Array<const char *> *ms = &global.params.modFileAliasStrings;
- const size_t msdim = ms ? ms->length : 0;
- for (size_t i = 0; i < packages->length; i++)
- {
- Identifier *pid = (*packages)[i];
- const char *p = pid->toChars();
- buf.writestring(p);
- if (msdim)
- checkModFileAlias(&buf, &dotmods, ms, msdim, p);
+ if (packages == NULL || packages->length == 0)
+ return filename;
+
+ OutBuffer buf;
+ OutBuffer dotmods;
+ Array<const char *> *ms = &global.params.modFileAliasStrings;
+ const size_t msdim = ms ? ms->length : 0;
+
+ for (size_t i = 0; i < packages->length; i++)
+ {
+ Identifier *pid = (*packages)[i];
+ const char *p = pid->toChars();
+ buf.writestring(p);
+ if (msdim)
+ checkModFileAlias(&buf, &dotmods, ms, msdim, p);
#if _WIN32
- buf.writeByte('\\');
+ buf.writeByte('\\');
#else
- buf.writeByte('/');
+ buf.writeByte('/');
#endif
- }
- buf.writestring(filename);
- if (msdim)
- checkModFileAlias(&buf, &dotmods, ms, msdim, filename);
- buf.writeByte(0);
- filename = (char *)buf.extractData();
}
+ buf.writestring(filename);
+ if (msdim)
+ checkModFileAlias(&buf, &dotmods, ms, msdim, filename);
+ buf.writeByte(0);
+ filename = (char *)buf.extractData();
- Module *m = new Module(filename, ident, 0, 0);
- m->loc = loc;
+ return filename;
+}
+
+/********************************************
+ * Look for the source file if it's different from filename.
+ * Look for .di, .d, directory, and along global.path.
+ * Does not open the file.
+ * Input:
+ * filename as supplied by the user
+ * global.path
+ * Returns:
+ * NULL if it's not different from filename.
+ */
- /* Look for the source file
+static const char *lookForSourceFile(const char *filename)
+{
+ /* Search along global.path for .di file, then .d file.
*/
- const char *path;
- const char *result = lookForSourceFile(&path, filename);
- if (result)
+ const char *sdi = FileName::forceExt(filename, global.hdr_ext.ptr);
+ if (FileName::exists(sdi) == 1)
+ return sdi;
+
+ const char *sd = FileName::forceExt(filename, global.mars_ext.ptr);
+ if (FileName::exists(sd) == 1)
+ return sd;
+
+ if (FileName::exists(filename) == 2)
{
- m->srcfile = new File(result);
- if (path)
- m->srcfilePath = path;
- else if (!FileName::absolute(result))
- m->srcfilePath = getcwd(NULL, 0);
+ /* The filename exists and it's a directory.
+ * Therefore, the result should be: filename/package.d
+ * iff filename/package.d is a file
+ */
+ const char *ni = FileName::combine(filename, "package.di");
+ if (FileName::exists(ni) == 1)
+ return ni;
+ FileName::free(ni);
+ const char *n = FileName::combine(filename, "package.d");
+ if (FileName::exists(n) == 1)
+ return n;
+ FileName::free(n);
+ }
+
+ if (FileName::absolute(filename))
+ return NULL;
+
+ if (!global.path)
+ return NULL;
+
+ for (size_t i = 0; i < global.path->length; i++)
+ {
+ const char *p = (*global.path)[i];
+ const char *n = FileName::combine(p, sdi);
+ if (FileName::exists(n) == 1)
+ {
+ return n;
+ }
+ FileName::free(n);
+
+ n = FileName::combine(p, sd);
+ if (FileName::exists(n) == 1)
+ {
+ return n;
+ }
+ FileName::free(n);
+
+ const char *b = FileName::removeExt(filename);
+ n = FileName::combine(p, b);
+ FileName::free(b);
+ if (FileName::exists(n) == 2)
+ {
+ const char *n2i = FileName::combine(n, "package.di");
+ if (FileName::exists(n2i) == 1)
+ return n2i;
+ FileName::free(n2i);
+ const char *n2 = FileName::combine(n, "package.d");
+ if (FileName::exists(n2) == 1)
+ {
+ return n2;
+ }
+ FileName::free(n2);
+ }
+ FileName::free(n);
}
+ return NULL;
+}
+
+Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident)
+{
+ //printf("Module::load(ident = '%s')\n", ident->toChars());
+
+ // Build module filename by turning:
+ // foo.bar.baz
+ // into:
+ // foo\bar\baz
+ const char *filename = getFilename(packages, ident);
+ // Look for the source file
+ const char *result = lookForSourceFile(filename);
+ if (result)
+ filename = result;
+
+ Module *m = new Module(filename, ident, 0, 0);
+ m->loc = loc;
if (!m->read(loc))
return NULL;
@@ -1159,6 +1246,27 @@ Module *Package::isPackageMod()
}
/**
+ * Checks for the existence of a package.d to set isPkgMod appropriately
+ * if isPkgMod == PKGunknown
+ */
+void Package::resolvePKGunknown()
+{
+ if (isModule())
+ return;
+ if (isPkgMod != PKGunknown)
+ return;
+
+ Identifiers packages;
+ for (Dsymbol *s = this->parent; s; s = s->parent)
+ packages.insert(0, s->ident);
+
+ if (lookForSourceFile(getFilename(&packages, ident)))
+ Module::load(Loc(), &packages, this->ident);
+ else
+ isPkgMod = PKGpackage;
+}
+
+/**
* Checks if pkg is a sub-package of this
*
* For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
@@ -1266,96 +1374,3 @@ Dsymbol *Package::search(const Loc &loc, Identifier *ident, int flags)
return ScopeDsymbol::search(loc, ident, flags);
}
-
-/* =========================== ===================== */
-
-/********************************************
- * Look for the source file if it's different from filename.
- * Look for .di, .d, directory, and along global.path.
- * Does not open the file.
- * Output:
- * path the path where the file was found if it was not the current directory
- * Input:
- * filename as supplied by the user
- * global.path
- * Returns:
- * NULL if it's not different from filename.
- */
-
-const char *lookForSourceFile(const char **path, const char *filename)
-{
- /* Search along global.path for .di file, then .d file.
- */
- *path = NULL;
-
- const char *sdi = FileName::forceExt(filename, global.hdr_ext.ptr);
- if (FileName::exists(sdi) == 1)
- return sdi;
-
- const char *sd = FileName::forceExt(filename, global.mars_ext.ptr);
- if (FileName::exists(sd) == 1)
- return sd;
-
- if (FileName::exists(filename) == 2)
- {
- /* The filename exists and it's a directory.
- * Therefore, the result should be: filename/package.d
- * iff filename/package.d is a file
- */
- const char *ni = FileName::combine(filename, "package.di");
- if (FileName::exists(ni) == 1)
- return ni;
- FileName::free(ni);
- const char *n = FileName::combine(filename, "package.d");
- if (FileName::exists(n) == 1)
- return n;
- FileName::free(n);
- }
-
- if (FileName::absolute(filename))
- return NULL;
-
- if (!global.path)
- return NULL;
-
- for (size_t i = 0; i < global.path->length; i++)
- {
- const char *p = (*global.path)[i];
-
- const char *n = FileName::combine(p, sdi);
- if (FileName::exists(n) == 1)
- {
- *path = p;
- return n;
- }
- FileName::free(n);
-
- n = FileName::combine(p, sd);
- if (FileName::exists(n) == 1)
- {
- *path = p;
- return n;
- }
- FileName::free(n);
-
- const char *b = FileName::removeExt(filename);
- n = FileName::combine(p, b);
- FileName::free(b);
- if (FileName::exists(n) == 2)
- {
- const char *n2i = FileName::combine(n, "package.di");
- if (FileName::exists(n2i) == 1)
- return n2i;
- FileName::free(n2i);
- const char *n2 = FileName::combine(n, "package.d");
- if (FileName::exists(n2) == 1)
- {
- *path = p;
- return n2;
- }
- FileName::free(n2);
- }
- FileName::free(n);
- }
- return NULL;
-}
diff --git a/gcc/d/dmd/dstruct.c b/gcc/d/dmd/dstruct.c
index 2b87154..8829367 100644
--- a/gcc/d/dmd/dstruct.c
+++ b/gcc/d/dmd/dstruct.c
@@ -23,6 +23,8 @@
#include "template.h"
#include "tokens.h"
#include "target.h"
+#include "utf.h"
+#include "root/ctfloat.h"
Type *getTypeInfoType(Loc loc, Type *t, Scope *sc);
void unSpeculative(Scope *sc, RootObject *o);
@@ -1245,6 +1247,102 @@ Dsymbol *StructDeclaration::search(const Loc &loc, Identifier *ident, int flags)
return ScopeDsymbol::search(loc, ident, flags);
}
+/**********************************
+ * Determine if exp is all binary zeros.
+ * Params:
+ * exp = expression to check
+ * Returns:
+ * true if it's all binary 0
+ */
+static bool isZeroInit(Expression *exp)
+{
+ switch (exp->op)
+ {
+ case TOKint64:
+ return exp->toInteger() == 0;
+
+ case TOKnull:
+ case TOKfalse:
+ return true;
+
+ case TOKstructliteral:
+ {
+ StructLiteralExp *sle = (StructLiteralExp *) exp;
+ for (size_t i = 0; i < sle->sd->fields.length; i++)
+ {
+ VarDeclaration *field = sle->sd->fields[i];
+ if (field->type->size(field->loc))
+ {
+ Expression *e = (*sle->elements)[i];
+ if (e ? !isZeroInit(e)
+ : !field->type->isZeroInit(field->loc))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case TOKarrayliteral:
+ {
+ ArrayLiteralExp *ale = (ArrayLiteralExp *) exp;
+
+ const size_t dim = ale->elements ? ale->elements->length : 0;
+
+ if (ale->type->toBasetype()->ty == Tarray) // if initializing a dynamic array
+ return dim == 0;
+
+ for (size_t i = 0; i < dim; i++)
+ {
+ if (!isZeroInit(ale->getElement(i)))
+ return false;
+ }
+ /* Note that true is returned for all T[0]
+ */
+ return true;
+ }
+
+ case TOKstring:
+ {
+ StringExp *se = exp->toStringExp();
+
+ if (se->type->toBasetype()->ty == Tarray) // if initializing a dynamic array
+ return se->len == 0;
+
+ void *s = se->string;
+ for (size_t i = 0; i < se->len; i++)
+ {
+ dinteger_t val;
+ switch (se->sz)
+ {
+ case 1: val = (( utf8_t *)s)[i]; break;
+ case 2: val = ((utf16_t *)s)[i]; break;
+ case 4: val = ((utf32_t *)s)[i]; break;
+ default: assert(0); break;
+ }
+ if (val)
+ return false;
+ }
+ return true;
+ }
+
+ case TOKvector:
+ {
+ VectorExp *ve = (VectorExp *) exp;
+ return isZeroInit(ve->e1);
+ }
+
+ case TOKfloat64:
+ case TOKcomplex80:
+ {
+ return (exp->toReal() == CTFloat::zero) &&
+ (exp->toImaginary() == CTFloat::zero);
+ }
+
+ default:
+ return false;
+ }
+}
+
void StructDeclaration::finalizeSize()
{
//printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
@@ -1301,9 +1399,23 @@ void StructDeclaration::finalizeSize()
VarDeclaration *vd = fields[i];
if (vd->_init)
{
- // Should examine init to see if it is really all 0's
- zeroInit = 0;
- break;
+ if (vd->_init->isVoidInitializer())
+ /* Treat as 0 for the purposes of putting the initializer
+ * in the BSS segment, or doing a mass set to 0
+ */
+ continue;
+
+ // Zero size fields are zero initialized
+ if (vd->type->size(vd->loc) == 0)
+ continue;
+
+ // Examine init to see if it is all 0s.
+ Expression *exp = vd->getConstInitializer();
+ if (!exp || !isZeroInit(exp))
+ {
+ zeroInit = 0;
+ break;
+ }
}
else if (!vd->type->isZeroInit(loc))
{
diff --git a/gcc/d/dmd/dtemplate.c b/gcc/d/dmd/dtemplate.c
index fe65bd2..1035f82 100644
--- a/gcc/d/dmd/dtemplate.c
+++ b/gcc/d/dmd/dtemplate.c
@@ -6783,7 +6783,7 @@ bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f
{
//printf("type %s\n", ta->toChars());
// It might really be an Expression or an Alias
- ta->resolve(loc, sc, &ea, &ta, &sa);
+ ta->resolve(loc, sc, &ea, &ta, &sa, (flags & 1) != 0);
if (ea) goto Lexpr;
if (sa) goto Ldsym;
if (ta == NULL)
@@ -6914,7 +6914,7 @@ bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f
//goto Ldsym;
}
}
- if (ea->op == TOKdotvar)
+ if (ea->op == TOKdotvar && !(flags & 1))
{
// translate expression to dsymbol.
sa = ((DotVarExp *)ea)->var;
@@ -6925,7 +6925,7 @@ bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f
sa = ((TemplateExp *)ea)->td;
goto Ldsym;
}
- if (ea->op == TOKdottd)
+ if (ea->op == TOKdottd && !(flags & 1))
{
// translate expression to dsymbol.
sa = ((DotTemplateExp *)ea)->td;
diff --git a/gcc/d/dmd/expression.c b/gcc/d/dmd/expression.c
index 09dd3af..7891832 100644
--- a/gcc/d/dmd/expression.c
+++ b/gcc/d/dmd/expression.c
@@ -3337,7 +3337,7 @@ ClassReferenceExp *Expression::isClassReferenceExp()
/****************************************
- * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__ to loc.
+ * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE__FULL_PATH__ to loc.
*/
Expression *Expression::resolveLoc(Loc, Scope *)
@@ -7170,9 +7170,12 @@ FileInitExp::FileInitExp(Loc loc, TOK tok)
Expression *FileInitExp::resolveLoc(Loc loc, Scope *sc)
{
//printf("FileInitExp::resolve() %s\n", toChars());
- const char *s = loc.filename ? loc.filename : sc->_module->ident->toChars();
+ const char *s;
if (subop == TOKfilefullpath)
- s = FileName::combine(sc->_module->srcfilePath, s);
+ s = FileName::toAbsolute(loc.filename != NULL ? loc.filename : sc->_module->srcfile->name->toChars());
+ else
+ s = loc.filename != NULL ? loc.filename : sc->_module->ident->toChars();
+
Expression *e = new StringExp(loc, const_cast<char *>(s));
e = semantic(e, sc);
e = e->castTo(sc, type);
diff --git a/gcc/d/dmd/expressionsem.c b/gcc/d/dmd/expressionsem.c
index d251996..7dfe995 100644
--- a/gcc/d/dmd/expressionsem.c
+++ b/gcc/d/dmd/expressionsem.c
@@ -119,6 +119,36 @@ static bool preFunctionParameters(Scope *sc, Expressions *exps)
return err;
}
+/**
+ * Determines whether a symbol represents a module or package
+ * (Used as a helper for is(type == module) and is(type == package))
+ *
+ * Params:
+ * sym = the symbol to be checked
+ *
+ * Returns:
+ * the symbol which `sym` represents (or `null` if it doesn't represent a `Package`)
+ */
+Package *resolveIsPackage(Dsymbol *sym)
+{
+ Package *pkg;
+ if (Import *imp = sym->isImport())
+ {
+ if (imp->pkg == NULL)
+ {
+ error(sym->loc, "Internal Compiler Error: unable to process forward-referenced import `%s`",
+ imp->toChars());
+ assert(0);
+ }
+ pkg = imp->pkg;
+ }
+ else
+ pkg = sym->isPackage();
+ if (pkg)
+ pkg->resolvePKGunknown();
+ return pkg;
+}
+
class ExpressionSemanticVisitor : public Visitor
{
public:
@@ -1920,15 +1950,34 @@ public:
}
Type *tded = NULL;
- Scope *sc2 = sc->copy(); // keep sc->flags
- sc2->tinst = NULL;
- sc2->minst = NULL;
- sc2->flags |= SCOPEfullinst;
- Type *t = e->targ->trySemantic(e->loc, sc2);
- sc2->pop();
- if (!t)
- goto Lno; // errors, so condition is false
- e->targ = t;
+ if (e->tok2 == TOKpackage || e->tok2 == TOKmodule) // These is() expressions are special because they can work on modules, not just types.
+ {
+ Dsymbol *sym = e->targ->toDsymbol(sc);
+ if (sym == NULL)
+ goto Lno;
+ Package *p = resolveIsPackage(sym);
+ if (p == NULL)
+ goto Lno;
+ if (e->tok2 == TOKpackage && p->isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module.
+ goto Lno;
+ else if(e->tok2 == TOKmodule && !(p->isModule() || p->isPackageMod()))
+ goto Lno;
+ tded = e->targ;
+ goto Lyes;
+ }
+
+ {
+ Scope *sc2 = sc->copy(); // keep sc->flags
+ sc2->tinst = NULL;
+ sc2->minst = NULL;
+ sc2->flags |= SCOPEfullinst;
+ Type *t = e->targ->trySemantic(e->loc, sc2);
+ sc2->pop();
+ if (!t) // errors, so condition is false
+ goto Lno;
+ e->targ = t;
+ }
+
if (e->tok2 != TOKreserved)
{
switch (e->tok2)
diff --git a/gcc/d/dmd/func.c b/gcc/d/dmd/func.c
index dbc5fa6..fe1ad11 100644
--- a/gcc/d/dmd/func.c
+++ b/gcc/d/dmd/func.c
@@ -41,7 +41,6 @@ Expression *semantic(Expression *e, Scope *sc);
int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow);
TypeIdentifier *getThrowable();
-RET retStyle(TypeFunction *tf);
void MODtoBuffer(OutBuffer *buf, MOD mod);
char *MODtoChars(MOD mod);
bool MODimplicitConv(MOD modfrom, MOD modto);
@@ -970,7 +969,7 @@ void FuncDeclaration::semantic(Scope *sc)
{
if (fdv->isFuture())
{
- ::deprecation(loc, "@future base class method %s is being overridden by %s; rename the latter",
+ ::deprecation(loc, "@__future base class method %s is being overridden by %s; rename the latter",
fdv->toPrettyChars(), toPrettyChars());
// Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[]
goto Lintro;
@@ -1758,7 +1757,7 @@ void FuncDeclaration::semantic3(Scope *sc)
if (storage_class & STCauto)
storage_class &= ~STCauto;
}
- if (retStyle(f) != RETstack || checkNrvo())
+ if (!target.isReturnOnStack(f, needThis()) || !checkNRVO())
nrvo_can = 0;
if (fbody->isErrorStatement())
@@ -4275,19 +4274,16 @@ void FuncDeclaration::checkDmain()
* using NRVO is possible.
*
* Returns:
- * true if the result cannot be returned by hidden reference.
+ * `false` if the result cannot be returned by hidden reference.
*/
-bool FuncDeclaration::checkNrvo()
+bool FuncDeclaration::checkNRVO()
{
- if (!nrvo_can)
- return true;
-
- if (returns == NULL)
- return true;
+ if (!nrvo_can || returns == NULL)
+ return false;
TypeFunction *tf = type->toTypeFunction();
if (tf->isref)
- return true;
+ return false;
for (size_t i = 0; i < returns->length; i++)
{
@@ -4297,24 +4293,23 @@ bool FuncDeclaration::checkNrvo()
{
VarDeclaration *v = ve->var->isVarDeclaration();
if (!v || v->isOut() || v->isRef())
- return true;
+ return false;
else if (nrvo_var == NULL)
{
- if (!v->isDataseg() && !v->isParameter() && v->toParent2() == this)
- {
- //printf("Setting nrvo to %s\n", v->toChars());
- nrvo_var = v;
- }
- else
- return true;
+ // Variables in the data segment (e.g. globals, TLS or not),
+ // parameters and closure variables cannot be NRVOed.
+ if (v->isDataseg() || v->isParameter() || v->toParent2() != this)
+ return false;
+ //printf("Setting nrvo to %s\n", v->toChars());
+ nrvo_var = v;
}
else if (nrvo_var != v)
- return true;
+ return false;
}
else //if (!exp->isLvalue()) // keep NRVO-ability
- return true;
+ return false;
}
- return false;
+ return true;
}
const char *FuncDeclaration::kind() const
diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h
index 6aff9b4..502bae2 100644
--- a/gcc/d/dmd/globals.h
+++ b/gcc/d/dmd/globals.h
@@ -286,7 +286,7 @@ typedef uint64_t d_uns64;
// file location
struct Loc
{
- const char *filename;
+ const char *filename; // either absolute or relative to cwd
unsigned linnum;
unsigned charnum;
diff --git a/gcc/d/dmd/idgen.c b/gcc/d/dmd/idgen.c
index 16f3b5f..09855a0 100644
--- a/gcc/d/dmd/idgen.c
+++ b/gcc/d/dmd/idgen.c
@@ -322,6 +322,9 @@ Msgtable msgtable[] =
{ "isFinalClass", NULL },
{ "isTemplate", NULL },
{ "isPOD", NULL },
+ { "isDeprecated", NULL },
+ { "isDisabled", NULL },
+ { "isFuture" , NULL },
{ "isNested", NULL },
{ "isFloating", NULL },
{ "isIntegral", NULL },
@@ -334,13 +337,17 @@ Msgtable msgtable[] =
{ "isFinalFunction", NULL },
{ "isOverrideFunction", NULL },
{ "isStaticFunction", NULL },
+ { "isModule", NULL },
+ { "isPackage", NULL },
{ "isRef", NULL },
{ "isOut", NULL },
{ "isLazy", NULL },
{ "hasMember", NULL },
{ "identifier", NULL },
{ "getProtection", NULL },
+ { "getVisibility", NULL },
{ "parent", NULL },
+ { "child", NULL },
{ "getMember", NULL },
{ "getOverloads", NULL },
{ "getVirtualFunctions", NULL },
@@ -360,6 +367,12 @@ Msgtable msgtable[] =
{ "getUnitTests", NULL },
{ "getVirtualIndex", NULL },
{ "getPointerBitmap", NULL },
+ { "isReturnOnStack", NULL },
+ { "isZeroInit", NULL },
+ { "getTargetInfo", NULL },
+ { "getLocation", NULL },
+ { "hasPostblit", NULL },
+ { "isCopyable", NULL },
// For C++ mangling
{ "allocator", NULL },
diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h
index 17ad590..4968ec7 100644
--- a/gcc/d/dmd/module.h
+++ b/gcc/d/dmd/module.h
@@ -48,6 +48,7 @@ public:
void accept(Visitor *v) { v->visit(this); }
Module *isPackageMod();
+ void resolvePKGunknown();
};
class Module : public Package
@@ -68,7 +69,6 @@ public:
const char *arg; // original argument name
ModuleDeclaration *md; // if !NULL, the contents of the ModuleDeclaration declaration
File *srcfile; // input source file
- const char* srcfilePath; // the path prefix to the srcfile if it applies
File *objfile; // output .obj file
File *hdrfile; // 'header' file
File *docfile; // output documentation file
diff --git a/gcc/d/dmd/mtype.c b/gcc/d/dmd/mtype.c
index 6f0195a..94e2082 100644
--- a/gcc/d/dmd/mtype.c
+++ b/gcc/d/dmd/mtype.c
@@ -6682,6 +6682,7 @@ Type *TypeTraits::semantic(Loc, Scope *sc)
exp->ident != Id::derivedMembers &&
exp->ident != Id::getMember &&
exp->ident != Id::parent &&
+ exp->ident != Id::child &&
exp->ident != Id::getOverloads &&
exp->ident != Id::getVirtualFunctions &&
exp->ident != Id::getVirtualMethods &&
diff --git a/gcc/d/dmd/parse.c b/gcc/d/dmd/parse.c
index 3e4dd06..be861fa 100644
--- a/gcc/d/dmd/parse.c
+++ b/gcc/d/dmd/parse.c
@@ -5949,7 +5949,10 @@ bool Parser::isDeclaration(Token *t, int needId, TOK endtok, Token **pt)
}
if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != 3))
goto Lisnot;
- if (needId == 1 || (needId == 0 && !haveId) || ((needId == 2 || needId == 3) && haveId))
+ if ((needId == 0 && !haveId) ||
+ (needId == 1) ||
+ (needId == 2 && haveId) ||
+ (needId == 3 && haveId))
{
if (pt)
*pt = t;
@@ -6821,12 +6824,8 @@ Expression *Parser::parsePrimaryExp()
case TOKfilefullpath:
{
- const char *srcfile = mod->srcfile->name->toChars();
- const char *s;
- if (loc.filename && !FileName::equals(loc.filename, srcfile))
- s = loc.filename;
- else
- s = FileName::combine(mod->srcfilePath, srcfile);
+ assert(loc.filename); // __FILE_FULL_PATH__ does not work with an invalid location
+ const char *s = FileName::toAbsolute(loc.filename);
e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
nextToken();
break;
@@ -7039,6 +7038,8 @@ Expression *Parser::parsePrimaryExp()
token.value == TOKsuper ||
token.value == TOKenum ||
token.value == TOKinterface ||
+ token.value == TOKmodule ||
+ token.value == TOKpackage ||
token.value == TOKargTypes ||
token.value == TOKparameters ||
(token.value == TOKconst && peek(&token)->value == TOKrparen) ||
diff --git a/gcc/d/dmd/root/filename.c b/gcc/d/dmd/root/filename.c
index 50b6740..f0e0213 100644
--- a/gcc/d/dmd/root/filename.c
+++ b/gcc/d/dmd/root/filename.c
@@ -175,6 +175,20 @@ bool FileName::absolute(const char *name)
#endif
}
+/**
+Return the given name as an absolute path
+
+Params:
+ name = path
+ base = the absolute base to prefix name with if it is relative
+
+Returns: name as an absolute path relative to base
+*/
+const char *FileName::toAbsolute(const char *name, const char *base)
+{
+ return absolute(name) ? name : combine(base ? base : getcwd(NULL, 0), name);
+}
+
/********************************
* Return filename extension (read-only).
* Points past '.' of extension.
diff --git a/gcc/d/dmd/root/filename.h b/gcc/d/dmd/root/filename.h
index 62a5a68..6ef515c 100644
--- a/gcc/d/dmd/root/filename.h
+++ b/gcc/d/dmd/root/filename.h
@@ -24,6 +24,7 @@ struct FileName
int compare(RootObject *obj);
static int compare(const char *name1, const char *name2);
static bool absolute(const char *name);
+ static const char *toAbsolute(const char *name, const char *base = NULL);
static const char *ext(const char *);
const char *ext();
static const char *removeExt(const char *str);
diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h
index f2a55d6..5a2dd4d 100644
--- a/gcc/d/dmd/target.h
+++ b/gcc/d/dmd/target.h
@@ -22,6 +22,7 @@ class Expression;
class FuncDeclaration;
class Parameter;
class Type;
+class TypeFunction;
class TypeTuple;
struct OutBuffer;
@@ -105,6 +106,8 @@ public:
// ABI and backend.
LINK systemLinkage();
TypeTuple *toArgTypes(Type *t);
+ bool isReturnOnStack(TypeFunction *tf, bool needsThis);
+ Expression *getTargetInfo(const char* name, const Loc& loc);
};
extern Target target;
diff --git a/gcc/d/dmd/traits.c b/gcc/d/dmd/traits.c
index bc1e2c3..6585299 100644
--- a/gcc/d/dmd/traits.c
+++ b/gcc/d/dmd/traits.c
@@ -32,11 +32,13 @@
#include "attrib.h"
#include "parse.h"
#include "root/speller.h"
+#include "target.h"
typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s);
int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn = NULL);
void freeFieldinit(Scope *sc);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
+Package *resolveIsPackage(Dsymbol *sym);
Expression *trySemantic(Expression *e, Scope *sc);
Expression *semantic(Expression *e, Scope *sc);
Expression *typeToExpression(Type *t);
@@ -49,32 +51,69 @@ Expression *typeToExpression(Type *t);
struct Ptrait
{
+ Dsymbol *sym;
Expression *e1;
Expressions *exps; // collected results
Identifier *ident; // which trait we're looking for
+ bool includeTemplates;
+ AA **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 {}
+ *==============================================
+ */
+static void insertInterfaceInheritedFunction(Ptrait *p, FuncDeclaration *fd, Expression *e)
+{
+ Identifier *signature = Identifier::idPool(fd->type->toChars());
+ //printf("%s - %s\n", fd->toChars, signature);
+ if (!dmd_aaGetRvalue(*p->funcTypeHash, (void *)signature))
+ {
+ bool* value = (bool*) dmd_aaGet(p->funcTypeHash, (void *)signature);
+ *value = true;
+ p->exps->push(e);
+ }
+}
+
static int fptraits(void *param, Dsymbol *s)
{
- FuncDeclaration *f = s->isFuncDeclaration();
- if (!f)
+ Ptrait *p = (Ptrait *)param;
+ if (p->includeTemplates)
+ {
+ p->exps->push(new DsymbolExp(Loc(),s, false));
+ return 0;
+ }
+ FuncDeclaration *fd = s->isFuncDeclaration();
+ if (!fd)
return 0;
- Ptrait *p = (Ptrait *)param;
- if (p->ident == Id::getVirtualFunctions && !f->isVirtual())
+ if (p->ident == Id::getVirtualFunctions && !fd->isVirtual())
return 0;
- if (p->ident == Id::getVirtualMethods && !f->isVirtualMethod())
+ if (p->ident == Id::getVirtualMethods && !fd->isVirtualMethod())
return 0;
Expression *e;
- FuncAliasDeclaration* ad = new FuncAliasDeclaration(f->ident, f, false);
- ad->protection = f->protection;
+ FuncAliasDeclaration* ad = new FuncAliasDeclaration(fd->ident, fd, false);
+ ad->protection = fd->protection;
if (p->e1)
e = new DotVarExp(Loc(), p->e1, ad, false);
else
e = new DsymbolExp(Loc(), ad, false);
- p->exps->push(e);
+ // if the parent is an interface declaration
+ // we must check for functions with the same signature
+ // in different inherited interfaces
+ if (p->sym && p->sym->isInterfaceDeclaration())
+ insertInterfaceInheritedFunction(p, fd, e);
+ else
+ p->exps->push(e);
return 0;
}
@@ -126,22 +165,111 @@ static void collectUnitTests(Dsymbols *symbols, AA *uniqueUnitTests, Expressions
}
}
+/***************************************************
+ * Determine if type t is copyable.
+ * Params:
+ * t = type to check
+ * Returns:
+ * true if we can copy it
+ */
+static bool isCopyable(Type *t)
+{
+ //printf("isCopyable() %s\n", t->toChars());
+ if (TypeStruct *ts = t->isTypeStruct())
+ {
+ if (ts->sym->postblit &&
+ (ts->sym->postblit->storage_class & STCdisable))
+ return false;
+ }
+ return true;
+}
+
/************************ TraitsExp ************************************/
static Expression *True(TraitsExp *e) { return new IntegerExp(e->loc, true, Type::tbool); }
static Expression *False(TraitsExp *e) { return new IntegerExp(e->loc, false, Type::tbool); }
-bool isTypeArithmetic(Type *t) { return t->isintegral() || t->isfloating(); }
-bool isTypeFloating(Type *t) { return t->isfloating(); }
-bool isTypeIntegral(Type *t) { return t->isintegral(); }
-bool isTypeScalar(Type *t) { return t->isscalar(); }
-bool isTypeUnsigned(Type *t) { return t->isunsigned(); }
-bool isTypeAssociativeArray(Type *t) { return t->toBasetype()->ty == Taarray; }
-bool isTypeStaticArray(Type *t) { return t->toBasetype()->ty == Tsarray; }
-bool isTypeAbstractClass(Type *t) { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); }
-bool isTypeFinalClass(Type *t) { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; }
-
-Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t))
+/**************************************
+ * Convert `Expression` or `Type` to corresponding `Dsymbol`,
+ * additionally strip 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.
+ */
+static Dsymbol *getDsymbolWithoutExpCtx(RootObject *oarg)
+{
+ if (Expression *e = isExpression(oarg))
+ {
+ if (e->op == TOKdotvar)
+ return ((DotVarExp *)e)->var;
+ if (e->op == TOKdottd)
+ return ((DotTemplateExp *)e)->td;
+ }
+ return getDsymbol(oarg);
+}
+
+/**
+ 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 = optional pointer to a function declararion, to be set
+ if `o` is a function declarartion.
+
+ 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, FuncDeclaration **fdp = NULL)
+{
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
+ Type *t = isType(o);
+ TypeFunction *tf = NULL;
+
+ if (s)
+ {
+ FuncDeclaration *fd = s->isFuncDeclaration();
+ if (fd)
+ {
+ t = fd->type;
+ if (fdp)
+ *fdp = fd;
+ }
+ else if (VarDeclaration *vd = s->isVarDeclaration())
+ t = vd->type;
+ }
+ if (t)
+ {
+ if (t->ty == Tfunction)
+ tf = (TypeFunction *)t;
+ else if (t->ty == Tdelegate)
+ tf = (TypeFunction *)t->nextOf();
+ else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
+ tf = (TypeFunction *)t->nextOf();
+ }
+
+ return tf;
+}
+
+static bool isTypeArithmetic(Type *t) { return t->isintegral() || t->isfloating(); }
+static bool isTypeFloating(Type *t) { return t->isfloating(); }
+static bool isTypeIntegral(Type *t) { return t->isintegral(); }
+static bool isTypeScalar(Type *t) { return t->isscalar(); }
+static bool isTypeUnsigned(Type *t) { return t->isunsigned(); }
+static bool isTypeAssociativeArray(Type *t) { return t->toBasetype()->ty == Taarray; }
+static bool isTypeStaticArray(Type *t) { return t->toBasetype()->ty == Tsarray; }
+static bool isTypeAbstractClass(Type *t) { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); }
+static bool isTypeFinalClass(Type *t) { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; }
+
+static Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t))
{
if (!e->args || !e->args->length)
return False(e);
@@ -154,20 +282,52 @@ Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t))
return True(e);
}
-bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); }
-bool isFuncVirtualFunction(FuncDeclaration *f) { return f->isVirtual(); }
-bool isFuncVirtualMethod(FuncDeclaration *f) { return f->isVirtualMethod(); }
-bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); }
-bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); }
-bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); }
+static bool isDsymDeprecated(Dsymbol *s) { return s->isDeprecated(); }
+
+static int fpisTemplate(void *, Dsymbol *s)
+{
+ if (s->isTemplateDeclaration())
+ return 1;
+
+ return 0;
+}
+
+bool isTemplate(Dsymbol *s)
+{
+ if (!s->toAlias()->isOverloadable())
+ return false;
+
+ return overloadApply(s, NULL, &fpisTemplate) != 0;
+}
+
+static Expression *isDsymX(TraitsExp *e, bool (*fp)(Dsymbol *s))
+{
+ if (!e->args || !e->args->length)
+ return False(e);
+ for (size_t i = 0; i < e->args->length; i++)
+ {
+ Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
+ if (!s || !fp(s))
+ return False(e);
+ }
+ return True(e);
+}
+
+static bool isFuncDisabled(FuncDeclaration *f) { return f->isDisabled(); }
+static bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); }
+static bool isFuncVirtualFunction(FuncDeclaration *f) { return f->isVirtual(); }
+static bool isFuncVirtualMethod(FuncDeclaration *f) { return f->isVirtualMethod(); }
+static bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); }
+static bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); }
+static bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); }
-Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f))
+static Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f))
{
if (!e->args || !e->args->length)
return False(e);
for (size_t i = 0; i < e->args->length; i++)
{
- Dsymbol *s = getDsymbol((*e->args)[i]);
+ Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
if (!s)
return False(e);
FuncDeclaration *f = s->isFuncDeclaration();
@@ -177,17 +337,18 @@ Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f))
return True(e);
}
-bool isDeclRef(Declaration *d) { return d->isRef(); }
-bool isDeclOut(Declaration *d) { return d->isOut(); }
-bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; }
+static bool isDeclFuture(Declaration *d) { return d->isFuture(); }
+static bool isDeclRef(Declaration *d) { return d->isRef(); }
+static bool isDeclOut(Declaration *d) { return d->isOut(); }
+static bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; }
-Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d))
+static Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d))
{
if (!e->args || !e->args->length)
return False(e);
for (size_t i = 0; i < e->args->length; i++)
{
- Dsymbol *s = getDsymbol((*e->args)[i]);
+ Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
if (!s)
return False(e);
Declaration *d = s->isDeclaration();
@@ -197,6 +358,25 @@ Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d))
return True(e);
}
+static bool isPkgModule(Package *p) { return p->isModule() || p->isPackageMod(); }
+static bool isPkgPackage(Package *p) { return p->isModule() == NULL; }
+
+static Expression *isPkgX(TraitsExp *e, bool (*fp)(Package *p))
+{
+ if (!e->args || !e->args->length)
+ return False(e);
+ for (size_t i = 0; i < e->args->length; i++)
+ {
+ Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
+ if (!s)
+ return False(e);
+ Package *p = resolveIsPackage(s);
+ if (!p || !fp(p))
+ return False(e);
+ }
+ return True(e);
+}
+
// callback for TypeFunction::attributesApply
struct PushAttributes
{
@@ -225,6 +405,9 @@ TraitsInitializer::TraitsInitializer()
"isAbstractClass",
"isArithmetic",
"isAssociativeArray",
+ "isDisabled",
+ "isDeprecated",
+ "isFuture",
"isFinalClass",
"isPOD",
"isNested",
@@ -239,13 +422,18 @@ TraitsInitializer::TraitsInitializer()
"isFinalFunction",
"isOverrideFunction",
"isStaticFunction",
+ "isModule",
+ "isPackage",
"isRef",
"isOut",
"isLazy",
+ "isReturnOnStack",
"hasMember",
"identifier",
"getProtection",
+ "getVisibility",
"parent",
+ "child",
"getLinkage",
"getMember",
"getOverloads",
@@ -265,10 +453,15 @@ TraitsInitializer::TraitsInitializer()
"getUnitTests",
"getVirtualIndex",
"getPointerBitmap",
+ "isZeroInit",
+ "getTargetInfo",
+ "getLocation",
+ "hasPostblit",
+ "isCopyable",
NULL
};
- traitsStringTable._init(40);
+ traitsStringTable._init(56);
for (size_t idx = 0;; idx++)
{
@@ -291,35 +484,6 @@ void *trait_search_fp(void *, const char *seed, int* cost)
return sv ? (void*)sv->ptrvalue : NULL;
}
-static int fpisTemplate(void *, Dsymbol *s)
-{
- if (s->isTemplateDeclaration())
- return 1;
-
- return 0;
-}
-
-bool isTemplate(Dsymbol *s)
-{
- if (!s->toAlias()->isOverloadable())
- return false;
-
- return overloadApply(s, NULL, &fpisTemplate) != 0;
-}
-
-Expression *isSymbolX(TraitsExp *e, bool (*fp)(Dsymbol *s))
-{
- if (!e->args || !e->args->length)
- return False(e);
- for (size_t i = 0; i < e->args->length; i++)
- {
- Dsymbol *s = getDsymbol((*e->args)[i]);
- if (!s || !fp(s))
- return False(e);
- }
- return True(e);
-}
-
/**
* get an array of size_t values that indicate possible pointer words in memory
* if interpreted as the type given as argument
@@ -491,11 +655,22 @@ static Expression *dimError(TraitsExp *e, int expected, int dim)
Expression *semanticTraits(TraitsExp *e, Scope *sc)
{
- 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 && e->ident != Id::getVisibility)
{
+ // Pretend we're in a deprecated scope so that deprecation messages
+ // aren't triggered when checking if a symbol is deprecated
+ const StorageClass save = sc->stc;
+ if (e->ident == Id::isDeprecated)
+ sc->stc |= STCdeprecated;
if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1))
+ {
+ sc->stc = save;
return new ErrorExp();
+ }
+ sc->stc = save;
}
size_t dim = e->args ? e->args->length : 0;
@@ -523,6 +698,14 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
{
return isTypeX(e, &isTypeAssociativeArray);
}
+ else if (e->ident == Id::isDeprecated)
+ {
+ return isDsymX(e, &isDsymDeprecated);
+ }
+ else if (e->ident == Id::isFuture)
+ {
+ return isDeclX(e, &isDeclFuture);
+ }
else if (e->ident == Id::isStaticArray)
{
return isTypeX(e, &isTypeStaticArray);
@@ -537,7 +720,10 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
else if (e->ident == Id::isTemplate)
{
- return isSymbolX(e, &isTemplate);
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ return isDsymX(e, &isTemplate);
}
else if (e->ident == Id::isPOD)
{
@@ -560,13 +746,50 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
return True(e);
}
+ else if (e->ident == Id::hasPostblit)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ RootObject *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();
+ if (StructDeclaration *sd = (tb->ty == Tstruct) ? ((TypeStruct *)tb)->sym : NULL)
+ {
+ return sd->postblit ? True(e) : False(e);
+ }
+ return False(e);
+ }
+ else if (e->ident == Id::isCopyable)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ RootObject *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();
+ }
+
+ return isCopyable(t) ? True(e) : False(e);
+ }
else if (e->ident == Id::isNested)
{
if (dim != 1)
return dimError(e, 1, dim);
RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
if (!s)
{
}
@@ -582,40 +805,88 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
e->error("aggregate or function expected instead of '%s'", o->toChars());
return new ErrorExp();
}
+ else if (e->ident == Id::isDisabled)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ return isFuncX(e, &isFuncDisabled);
+ }
else if (e->ident == Id::isAbstractFunction)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isFuncX(e, &isFuncAbstractFunction);
}
else if (e->ident == Id::isVirtualFunction)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isFuncX(e, &isFuncVirtualFunction);
}
else if (e->ident == Id::isVirtualMethod)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isFuncX(e, &isFuncVirtualMethod);
}
else if (e->ident == Id::isFinalFunction)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isFuncX(e, &isFuncFinalFunction);
}
else if (e->ident == Id::isOverrideFunction)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isFuncX(e, &isFuncOverrideFunction);
}
else if (e->ident == Id::isStaticFunction)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isFuncX(e, &isFuncStaticFunction);
}
+ else if (e->ident == Id::isModule)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ return isPkgX(e, &isPkgModule);
+ }
+ else if (e->ident == Id::isPackage)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ return isPkgX(e, &isPkgPackage);
+ }
else if (e->ident == Id::isRef)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isDeclX(e, &isDeclRef);
}
else if (e->ident == Id::isOut)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isDeclX(e, &isDeclOut);
}
else if (e->ident == Id::isLazy)
{
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
return isDeclX(e, &isDeclLazy);
}
else if (e->ident == Id::identifier)
@@ -639,7 +910,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
else
{
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
if (!s || !s->ident)
{
e->error("argument %s has no identifier", o->toChars());
@@ -651,7 +922,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
StringExp *se = new StringExp(e->loc, const_cast<char *>(id->toChars()));
return semantic(se, sc);
}
- else if (e->ident == Id::getProtection)
+ else if (e->ident == Id::getProtection || e->ident == Id::getVisibility)
{
if (dim != 1)
return dimError(e, 1, dim);
@@ -664,7 +935,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
return new ErrorExp();
RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
if (!s)
{
if (!isError(o))
@@ -685,7 +956,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
return dimError(e, 1, dim);
RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
if (s)
{
if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943
@@ -720,13 +991,51 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
return resolve(e->loc, sc, s, false);
}
+ else if (e->ident == Id::child)
+ {
+ if (dim != 2)
+ return dimError(e, 2, dim);
+
+ Expression *ex;
+ RootObject *op = (*e->args)[0];
+ if (Dsymbol *symp = getDsymbol(op))
+ ex = new DsymbolExp(e->loc, symp);
+ else if (Expression *exp = isExpression(op))
+ ex = exp;
+ else
+ {
+ e->error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op->toChars());
+ return new ErrorExp();
+ }
+
+ ex = semantic(ex, sc);
+ RootObject *oc = (*e->args)[1];
+ Dsymbol *symc = getDsymbol(oc);
+ if (!symc)
+ {
+ e->error("symbol expected as second argument of __traits `child` instead of `%s`", oc->toChars());
+ return new ErrorExp();
+ }
+
+ if (Declaration *d = symc->isDeclaration())
+ ex = new DotVarExp(e->loc, ex, d);
+ else if (TemplateDeclaration *td = symc->isTemplateDeclaration())
+ ex = new DotExp(e->loc, ex, new TemplateExp(e->loc, td));
+ else if (ScopeDsymbol *ti = symc->isScopeDsymbol())
+ ex = new DotExp(e->loc, ex, new ScopeExp(e->loc, ti));
+ else
+ assert(0);
+
+ ex = semantic(ex, sc);
+ return ex;
+ }
else if (e->ident == Id::hasMember ||
e->ident == Id::getMember ||
e->ident == Id::getOverloads ||
e->ident == Id::getVirtualMethods ||
e->ident == Id::getVirtualFunctions)
{
- if (dim != 2)
+ if (dim != 2 && !(dim == 3 && e->ident == Id::getOverloads))
return dimError(e, 2, dim);
RootObject *o = (*e->args)[0];
@@ -738,6 +1047,19 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
ex = ex->ctfeInterpret();
+ bool includeTemplates = false;
+ if (dim == 3 && e->ident == Id::getOverloads)
+ {
+ Expression *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 new ErrorExp();
+ }
+ includeTemplates = b->isBool(true);
+ }
+
StringExp *se = ex->toStringExp();
if (!se || se->len == 0)
{
@@ -758,6 +1080,11 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
Dsymbol *sym = getDsymbol(o);
if (sym)
{
+ if (e->ident == Id::hasMember)
+ {
+ if (sym->search(e->loc, id) != NULL)
+ return True(e);
+ }
ex = new DsymbolExp(e->loc, sym);
ex = new DotIdExp(e->loc, ex, id);
}
@@ -773,12 +1100,6 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
if (e->ident == Id::hasMember)
{
- if (sym)
- {
- if (sym->search(e->loc, id))
- return True(e);
- }
-
/* Take any errors as meaning it wasn't found
*/
Scope *scx = sc->push();
@@ -814,7 +1135,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
/* Create tuple of functions of ex
*/
Expressions *exps = new Expressions();
- FuncDeclaration *f;
+ Dsymbol *f;
if (ex->op == TOKvar)
{
VarExp *ve = (VarExp *)ex;
@@ -830,13 +1151,43 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
else
ex = dve->e1;
}
+ else if (ex->op == TOKtemplate)
+ {
+ TemplateExp *te = (TemplateExp *)ex;
+ TemplateDeclaration *td = te->td;
+ f = td;
+ if (td && td->funcroot)
+ f = td->funcroot;
+ ex = NULL;
+ }
else
f = NULL;
Ptrait p;
+ p.sym = sym;
p.exps = exps;
p.e1 = ex;
p.ident = e->ident;
- overloadApply(f, &p, &fptraits);
+ p.includeTemplates = includeTemplates;
+ AA *funcTypeHash = NULL;
+ p.funcTypeHash = &funcTypeHash;
+
+ InterfaceDeclaration *ifd = NULL;
+ if (sym)
+ ifd = sym->isInterfaceDeclaration();
+ // If the symbol passed as a parameter is an
+ // interface that inherits other interfaces
+ if (ifd && ifd->interfaces.length)
+ {
+ // check the overloads of each inherited interface individually
+ for (size_t i = 0; i < ifd->interfaces.length; i++)
+ {
+ BaseClass *bc = ifd->interfaces.ptr[i];
+ if (Dsymbol *fd = bc->sym->search(e->loc, f->ident))
+ overloadApply(fd, &p, &fptraits);
+ }
+ }
+ else
+ overloadApply(f, &p, &fptraits);
ex = new TupleExp(e->loc, exps);
ex = semantic(ex, scx);
@@ -898,7 +1249,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
return dimError(e, 1, dim);
RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
if (!s)
{
e->error("first argument is not a symbol");
@@ -917,30 +1268,14 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
else 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.
+ * https://dlang.org/spec/traits.html#getFunctionAttributes
+ */
if (dim != 1)
return dimError(e, 1, dim);
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
- Type *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 (t)
- {
- if (t->ty == Tfunction)
- tf = (TypeFunction *)t;
- else if (t->ty == Tdelegate)
- tf = (TypeFunction *)t->nextOf();
- else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
- tf = (TypeFunction *)t->nextOf();
- }
+ TypeFunction *tf = toTypeFunction((*e->args)[0]);
+
if (!tf)
{
e->error("first argument is not a function");
@@ -956,6 +1291,27 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
TupleExp *tup = new TupleExp(e->loc, mods);
return semantic(tup, sc);
}
+ else 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(e, 1, dim);
+
+ RootObject *o = (*e->args)[0];
+ FuncDeclaration *fd = NULL;
+ TypeFunction *tf = toTypeFunction(o, &fd);
+
+ if (!tf)
+ {
+ e->error("argument to `__traits(isReturnOnStack, %s)` is not a function", o->toChars());
+ return new ErrorExp();
+ }
+
+ bool value = target.isReturnOnStack(tf, fd && fd->needThis());
+ return new IntegerExp(e->loc, value, Type::tbool);
+ }
else if (e->ident == Id::getFunctionVariadicStyle)
{
/* Accept a symbol or a type. Returns one of the following:
@@ -971,17 +1327,9 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
LINK link;
VarArg varargs;
RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- TypeFunction *tf = NULL;
- if (t)
- {
- if (t->ty == Tfunction)
- tf = (TypeFunction *)t;
- else if (t->ty == Tdelegate)
- tf = (TypeFunction *)t->nextOf();
- else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
- tf = (TypeFunction *)t->nextOf();
- }
+ FuncDeclaration *fd = NULL;
+ TypeFunction *tf = toTypeFunction(o, &fd);
+
if (tf)
{
link = tf->linkage;
@@ -989,9 +1337,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
else
{
- Dsymbol *s = getDsymbol(o);
- FuncDeclaration *fd = NULL;
- if (!s || (fd = s->isFuncDeclaration()) == NULL)
+ if (!fd)
{
e->error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o->toChars());
return new ErrorExp();
@@ -1021,19 +1367,12 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
if (dim != 2)
return dimError(e, 2, dim);
- RootObject *o1 = (*e->args)[1];
RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- TypeFunction *tf = NULL;
- if (t)
- {
- if (t->ty == Tfunction)
- tf = (TypeFunction *)t;
- else if (t->ty == Tdelegate)
- tf = (TypeFunction *)t->nextOf();
- else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
- tf = (TypeFunction *)t->nextOf();
- }
+ RootObject *o1 = (*e->args)[1];
+
+ FuncDeclaration *fd = NULL;
+ TypeFunction *tf = toTypeFunction(o, &fd);
+
ParameterList fparams;
if (tf)
{
@@ -1041,9 +1380,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
}
else
{
- Dsymbol *s = getDsymbol(o);
- FuncDeclaration *fd = NULL;
- if (!s || (fd = s->isFuncDeclaration()) == NULL)
+ if (!fd)
{
e->error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
o->toChars(), o1->toChars());
@@ -1118,17 +1455,9 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
LINK link;
RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- TypeFunction *tf = NULL;
- if (t)
- {
- if (t->ty == Tfunction)
- tf = (TypeFunction *)t;
- else if (t->ty == Tdelegate)
- tf = (TypeFunction *)t->nextOf();
- else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
- tf = (TypeFunction *)t->nextOf();
- }
+
+ TypeFunction *tf = toTypeFunction(o);
+
if (tf)
link = tf->linkage;
else
@@ -1421,7 +1750,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
return dimError(e, 1, dim);
RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
if (!s)
{
e->error("argument %s to __traits(getUnitTests) must be a module or aggregate",
@@ -1455,7 +1784,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
return dimError(e, 1, dim);
RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
+ Dsymbol *s = getDsymbolWithoutExpCtx(o);
FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
if (!fd)
@@ -1471,6 +1800,75 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
{
return pointerBitmap(e);
}
+ else if (e->ident == Id::isZeroInit)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ RootObject *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(e) : False(e);
+ }
+ else if (e->ident == Id::getTargetInfo)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+
+ Expression *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 new ErrorExp();
+ }
+ se = se->toUTF8(sc);
+
+ Expression *r = target.getTargetInfo(se->toPtr(), e->loc);
+ if (!r)
+ {
+ e->error("`getTargetInfo` key `\"%s\"` not supported by this implementation", se->toPtr());
+ return new ErrorExp();
+ }
+ return semantic(r, sc);
+ }
+ else if (e->ident == Id::getLocation)
+ {
+ if (dim != 1)
+ return dimError(e, 1, dim);
+ RootObject *arg0 = (*e->args)[0];
+ Dsymbol *s = getDsymbolWithoutExpCtx(arg0);
+ if (!s || !s->loc.filename)
+ {
+ e->error("can only get the location of a symbol, not `%s`", arg0->toChars());
+ return new ErrorExp();
+ }
+
+ const FuncDeclaration *fd = s->isFuncDeclaration();
+ if (fd && fd->overnext)
+ {
+ e->error("cannot get location of an overload set, "
+ "use `__traits(getOverloads, ..., \"%s\"%s)[N]` "
+ "to get the Nth overload",
+ arg0->toChars(), "");
+ return new ErrorExp();
+ }
+
+ Expressions *exps = new Expressions();
+ exps->setDim(3);
+ (*exps)[0] = new StringExp(e->loc, const_cast<char *>(s->loc.filename), strlen(s->loc.filename));
+ (*exps)[1] = new IntegerExp(e->loc, s->loc.linnum, Type::tint32);
+ (*exps)[2] = new IntegerExp(e->loc, s->loc.charnum, Type::tint32);
+ TupleExp *tup = new TupleExp(e->loc, exps);
+ return semantic(tup, sc);
+ }
if (const char *sub = (const char *)speller(e->ident->toChars(), &trait_search_fp, NULL, idchars))
e->error("unrecognized trait '%s', did you mean '%s'?", e->ident->toChars(), sub);