From: Iain Buclaw Date: Fri, 13 Mar 2020 20:03:02 +0000 (+0100) Subject: d/dmd: Merge upstream dmd e9420cfbf X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5b74dd0a2278365eb562d9d1999c3c11cddb733c;p=gcc.git d/dmd: Merge upstream dmd e9420cfbf 1. Implement DIP 1010 - (Static foreach) Support for 'static foreach' has been added. 'static foreach' is a conditional compilation construct that is to 'foreach' what 'static if' is to 'if'. It is a convenient way to generate declarations and statements by iteration. import std.conv: to; static foreach(i; 0 .. 10) { // a 'static foreach' body does not introduce a nested scope // (similar to 'static if'). // The following mixin declaration is at module scope: // declares 10 variables x0, x1, ..., x9 mixin('enum x' ~ to!string(i) ~ ' = i;'); } import std.range: iota; // all aggregate types that can be iterated with a standard 'foreach' // loop are also supported by static foreach: static foreach(i; iota(10)) { // we access the declarations generated in the first 'static foreach' pragma(msg, "x", i, ": ", mixin(`x` ~ to!string(i))); static assert(mixin(`x` ~ to!string(i)) == i); } void main() { import std.conv: text; import std.typecons: tuple; import std.algorithm: map; import std.stdio: writeln; // 'static foreach' has both declaration and statement forms // (similar to 'static if'). static foreach(x; iota(3).map!(i => tuple(text("x", i), i))) { // generates three local variables x0, x1 and x2. mixin(text(`int `,x[0],` = x[1];`)); scope(exit) // this is within the scope of 'main' { writeln(mixin(x[0])); } } writeln(x0," ",x1," ",x2); // first runtime output } 2. Aliases can be created directly from a '__trait'. Aliases can be created directly from the traits that return symbol(s) or tuples. This includes 'getMember', 'allMembers', 'derivedMembers', 'parent', 'getOverloads', 'getVirtualFunctions', 'getVirtualMethods', 'getUnitTests', 'getAttributes' and finally 'getAliasThis'. Previously an 'AliasSeq' was necessary in order to alias their return. Now the grammar allows to write shorter declarations: struct Foo { static int a; } alias oldWay = AliasSeq!(__traits(getMember, Foo, "a"))[0]; alias newWay = __traits(getMember, Foo, "a"); To permit this it was more interesting to include '__trait' in the basic types rather than just changing the alias syntax. So additionally, wherever a type appears a '__trait' can be used, for example in a variable declaration: struct Foo { static struct Bar {} } const(__traits(getMember, Foo, "Bar")) fooBar; static assert(is(typeof(fooBar) == const(Foo.Bar))); 3. fix Issue 10100 - Identifiers with double underscores and allMembers The identifer whitelist has been converted into a blacklist of all possible internal D language declarations. Reviewed-on: https://github.com/dlang/dmd/pull/10791 --- diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 578f3fc0309..b017c037d74 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -b37a537d36c2ac69afa505a3110e2328c9fc0114 +e9420cfbf5cd0cf9e6e398603e009ccc8e14d324 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/attrib.c b/gcc/d/dmd/attrib.c index 6cd715ce08b..86485d27616 100644 --- a/gcc/d/dmd/attrib.c +++ b/gcc/d/dmd/attrib.c @@ -31,6 +31,7 @@ bool definitelyValueParameter(Expression *e); Expression *semantic(Expression *e, Scope *sc); StringExp *semanticString(Scope *sc, Expression *exp, const char *s); +Dsymbols *makeTupleForeachStaticDecl(Scope *sc, ForeachStatement *fs, Dsymbols *dbody, bool needExpansion); /********************************* AttribDeclaration ****************************/ @@ -42,6 +43,9 @@ AttribDeclaration::AttribDeclaration(Dsymbols *decl) Dsymbols *AttribDeclaration::include(Scope *, ScopeDsymbol *) { + if (errors) + return NULL; + return decl; } @@ -752,6 +756,7 @@ void AnonDeclaration::semantic(Scope *sc) { ::error(loc, "%s can only be a part of an aggregate, not %s %s", kind(), p->kind(), p->toChars()); + errors = true; return; } @@ -1219,6 +1224,10 @@ bool ConditionalDeclaration::oneMember(Dsymbol **ps, Identifier *ident) Dsymbols *ConditionalDeclaration::include(Scope *sc, ScopeDsymbol *sds) { //printf("ConditionalDeclaration::include(sc = %p) _scope = %p\n", sc, _scope); + + if (errors) + return NULL; + assert(condition); return condition->include(_scope ? _scope : sc, sds) ? decl : elsedecl; } @@ -1275,6 +1284,7 @@ StaticIfDeclaration::StaticIfDeclaration(Condition *condition, //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); scopesym = NULL; addisdone = false; + onStack = false; } Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s) @@ -1293,12 +1303,17 @@ Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *) { //printf("StaticIfDeclaration::include(sc = %p) _scope = %p\n", sc, _scope); + if (errors || onStack) + return NULL; + onStack = true; + Dsymbols *d; + if (condition->inc == 0) { assert(scopesym); // addMember is already done assert(_scope); // setScope is already done - Dsymbols *d = ConditionalDeclaration::include(_scope, scopesym); + d = ConditionalDeclaration::include(_scope, scopesym); if (d && !addisdone) { @@ -1318,11 +1333,14 @@ Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *) addisdone = true; } + onStack = false; return d; } else { - return ConditionalDeclaration::include(sc, scopesym); + d = ConditionalDeclaration::include(sc, scopesym); + onStack = false; + return d; } } @@ -1366,6 +1384,173 @@ const char *StaticIfDeclaration::kind() const return "static if"; } +/***************************** StaticForeachDeclaration ***********************/ + +/* Static foreach at declaration scope, like: + * static foreach (i; [0, 1, 2]){ } + */ + +StaticForeachDeclaration::StaticForeachDeclaration(StaticForeach *sfe, Dsymbols *decl) + : AttribDeclaration(decl) +{ + this->sfe = sfe; + this->scopesym = NULL; + this->onStack = false; + this->cached = false; + this->cache = NULL; +} + +Dsymbol *StaticForeachDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + return new StaticForeachDeclaration( + sfe->syntaxCopy(), + Dsymbol::arraySyntaxCopy(decl)); +} + +bool StaticForeachDeclaration::oneMember(Dsymbol **ps, Identifier *ident) +{ + // Required to support IFTI on a template that contains a + // `static foreach` declaration. `super.oneMember` calls + // include with a `null` scope. As `static foreach` requires + // the scope for expansion, `oneMember` can only return a + // precise result once `static foreach` has been expanded. + if (cached) + { + return AttribDeclaration::oneMember(ps, ident); + } + *ps = NULL; // a `static foreach` declaration may in general expand to multiple symbols + return false; +} + +Dsymbols *StaticForeachDeclaration::include(Scope *, ScopeDsymbol *) +{ + if (errors || onStack) + return NULL; + if (cached) + { + assert(!onStack); + return cache; + } + onStack = true; + + if (_scope) + { + staticForeachPrepare(sfe, _scope); // lower static foreach aggregate + } + if (!staticForeachReady(sfe)) + { + onStack = false; + return NULL; // TODO: ok? + } + + // expand static foreach + Dsymbols *d = makeTupleForeachStaticDecl(_scope, sfe->aggrfe, decl, sfe->needExpansion); + if (d) // process generated declarations + { + // Add members lazily. + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + s->addMember(_scope, scopesym); + } + // Set the member scopes lazily. + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + s->setScope(_scope); + } + } + onStack = false; + cached = true; + cache = d; + return d; +} + +void StaticForeachDeclaration::addMember(Scope *, ScopeDsymbol *sds) +{ + // used only for caching the enclosing symbol + this->scopesym = sds; +} + +void StaticForeachDeclaration::addComment(const utf8_t *) +{ + // do nothing + // change this to give semantics to documentation comments on static foreach declarations +} + +void StaticForeachDeclaration::setScope(Scope *sc) +{ + // do not evaluate condition before semantic pass + // But do set the scope, in case we need it for forward referencing + Dsymbol::setScope(sc); +} + +void StaticForeachDeclaration::importAll(Scope *) +{ + // do not evaluate aggregate before semantic pass +} + +void StaticForeachDeclaration::semantic(Scope *sc) +{ + AttribDeclaration::semantic(sc); +} + +const char *StaticForeachDeclaration::kind() const +{ + return "static foreach"; +} + +/*********************************************************** + * Collection of declarations that stores foreach index variables in a + * local symbol table. Other symbols declared within are forwarded to + * another scope, like: + * + * static foreach (i; 0 .. 10) // loop variables for different indices do not conflict. + * { // this body is expanded into 10 ForwardingAttribDeclarations, where `i` has storage class STClocal + * mixin("enum x" ~ to!string(i) ~ " = i"); // ok, can access current loop variable + * } + * + * static foreach (i; 0.. 10) + * { + * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope + * } + * + * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop + * + * A StaticForeachDeclaration generates one + * ForwardingAttribDeclaration for each expansion of its body. The + * AST of the ForwardingAttribDeclaration contains both the `static + * foreach` variables and the respective copy of the `static foreach` + * body. The functionality is achieved by using a + * ForwardingScopeDsymbol as the parent symbol for the generated + * declarations. + */ + +ForwardingAttribDeclaration::ForwardingAttribDeclaration(Dsymbols *decl) + : AttribDeclaration(decl) +{ + sym = new ForwardingScopeDsymbol(NULL); + sym->symtab = new DsymbolTable(); +} + +/************************************** + * Use the ForwardingScopeDsymbol as the parent symbol for members. + */ +Scope *ForwardingAttribDeclaration::newScope(Scope *sc) +{ + return sc->push(sym); +} + +/*************************************** + * Lazily initializes the scope to forward to. + */ +void ForwardingAttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) +{ + parent = sym->parent = sym->forward = sds; + return AttribDeclaration::addMember(sc, sym); +} + /***************************** CompileDeclaration *****************************/ // These are mixin declarations, like mixin("int x"); diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h index d1f265a84b9..ccfcddadaca 100644 --- a/gcc/d/dmd/attrib.h +++ b/gcc/d/dmd/attrib.h @@ -191,6 +191,7 @@ class StaticIfDeclaration : public ConditionalDeclaration public: ScopeDsymbol *scopesym; bool addisdone; + bool onStack; StaticIfDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl); Dsymbol *syntaxCopy(Dsymbol *s); @@ -203,14 +204,16 @@ public: void accept(Visitor *v) { v->visit(this); } }; -class StaticForeachDeclaration : public ConditionalDeclaration +class StaticForeachDeclaration : public AttribDeclaration { public: StaticForeach *sfe; ScopeDsymbol *scopesym; + bool onStack; bool cached; Dsymbols *cache; + StaticForeachDeclaration(StaticForeach *sfe, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); bool oneMember(Dsymbol **ps, Identifier *ident); Dsymbols *include(Scope *sc, ScopeDsymbol *sds); @@ -223,14 +226,16 @@ public: void accept(Visitor *v) { v->visit(this); } }; -class ForwardingAttribDeclaration : AttribDeclaration +class ForwardingAttribDeclaration : public AttribDeclaration { public: ForwardingScopeDsymbol *sym; + ForwardingAttribDeclaration(Dsymbols *decl); Scope *newScope(Scope *sc); void addMember(Scope *sc, ScopeDsymbol *sds); ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return this; } + void accept(Visitor *v) { v->visit(this); } }; // Mixin declarations diff --git a/gcc/d/dmd/cond.c b/gcc/d/dmd/cond.c index 9d7df5fd240..c75399d3825 100644 --- a/gcc/d/dmd/cond.c +++ b/gcc/d/dmd/cond.c @@ -13,6 +13,7 @@ #include "mars.h" #include "id.h" #include "init.h" +#include "aggregate.h" #include "declaration.h" #include "identifier.h" #include "expression.h" @@ -21,6 +22,7 @@ #include "template.h" #include "mtype.h" #include "scope.h" +#include "statement.h" #include "arraytypes.h" #include "tokens.h" @@ -53,6 +55,327 @@ Condition::Condition(Loc loc) /* ============================================================ */ +StaticForeach::StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe) +{ + assert(!!aggrfe ^ !!rangefe); + this->loc = loc; + this->aggrfe = aggrfe; + this->rangefe = rangefe; + this->needExpansion = false; +} + +StaticForeach *StaticForeach::syntaxCopy() +{ + return new StaticForeach( + loc, + aggrfe ? (ForeachStatement *)aggrfe->syntaxCopy() : NULL, + rangefe ? (ForeachRangeStatement *)rangefe->syntaxCopy() : NULL + ); +} + +/***************************************** + * Turn an aggregate which is an array into an expression tuple + * of its elements. I.e., lower + * static foreach (x; [1, 2, 3, 4]) { ... } + * to + * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } + */ + +static void lowerArrayAggregate(StaticForeach *sfe, Scope *sc) +{ + Expression *aggr = sfe->aggrfe->aggr; + Expression *el = new ArrayLengthExp(aggr->loc, aggr); + sc = sc->startCTFE(); + el = semantic(el, sc); + sc = sc->endCTFE(); + el = el->optimize(WANTvalue); + el = el->ctfeInterpret(); + if (el->op == TOKint64) + { + dinteger_t length = el->toInteger(); + Expressions *es = new Expressions(); + for (size_t i = 0; i < length; i++) + { + IntegerExp *index = new IntegerExp(sfe->loc, i, Type::tsize_t); + Expression *value = new IndexExp(aggr->loc, aggr, index); + es->push(value); + } + sfe->aggrfe->aggr = new TupleExp(aggr->loc, es); + sfe->aggrfe->aggr = semantic(sfe->aggrfe->aggr, sc); + sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue); + } + else + { + sfe->aggrfe->aggr = new ErrorExp(); + } +} + +/***************************************** + * Wrap a statement into a function literal and call it. + * + * Params: + * loc = The source location. + * s = The statement. + * Returns: + * AST of the expression `(){ s; }()` with location loc. + */ + +static Expression *wrapAndCall(Loc loc, Statement *s) +{ + TypeFunction *tf = new TypeFunction(new Parameters(), NULL, 0, LINKdefault, 0); + FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, loc, tf, TOKreserved, NULL); + fd->fbody = s; + FuncExp *fe = new FuncExp(loc, fd); + Expression *ce = new CallExp(loc, fe, new Expressions()); + return ce; +} + +/***************************************** + * Create a `foreach` statement from `aggrefe/rangefe` with given + * `foreach` variables and body `s`. + * + * Params: + * loc = The source location. + * parameters = The foreach variables. + * s = The `foreach` body. + * Returns: + * `foreach (parameters; aggregate) s;` or + * `foreach (parameters; lower .. upper) s;` + * Where aggregate/lower, upper are as for the current StaticForeach. + */ + +static Statement *createForeach(StaticForeach *sfe, Loc loc, Parameters *parameters, Statement *s) +{ + if (sfe->aggrfe) + { + return new ForeachStatement(loc, sfe->aggrfe->op, parameters, sfe->aggrfe->aggr->syntaxCopy(), s, loc); + } + else + { + assert(sfe->rangefe && parameters->dim == 1); + return new ForeachRangeStatement(loc, sfe->rangefe->op, (*parameters)[0], + sfe->rangefe->lwr->syntaxCopy(), + sfe->rangefe->upr->syntaxCopy(), s, loc); + } +} + +/***************************************** + * For a `static foreach` with multiple loop variables, the + * aggregate is lowered to an array of tuples. As D does not have + * built-in tuples, we need a suitable tuple type. This generates + * a `struct` that serves as the tuple type. This type is only + * used during CTFE and hence its typeinfo will not go to the + * object file. + * + * Params: + * loc = The source location. + * e = The expressions we wish to store in the tuple. + * sc = The current scope. + * Returns: + * A struct type of the form + * struct Tuple + * { + * typeof(AliasSeq!(e)) tuple; + * } + */ + +static TypeStruct *createTupleType(Loc loc, Expressions *e) +{ // TODO: move to druntime? + Identifier *sid = Identifier::generateId("Tuple"); + StructDeclaration *sdecl = new StructDeclaration(loc, sid, false); + sdecl->storage_class |= STCstatic; + sdecl->members = new Dsymbols(); + Identifier *fid = Identifier::idPool("tuple"); + Type *ty = new TypeTypeof(loc, new TupleExp(loc, e)); + sdecl->members->push(new VarDeclaration(loc, ty, fid, NULL)); + TypeStruct *r = (TypeStruct *)sdecl->type; + r->vtinfo = TypeInfoStructDeclaration::create(r); // prevent typeinfo from going to object file + return r; +} + +/***************************************** + * Create the AST for an instantiation of a suitable tuple type. + * + * Params: + * loc = The source location. + * type = A Tuple type, created with createTupleType. + * e = The expressions we wish to store in the tuple. + * Returns: + * An AST for the expression `Tuple(e)`. + */ + +static Expression *createTuple(Loc loc, TypeStruct *type, Expressions *e) +{ // TODO: move to druntime? + return new CallExp(loc, new TypeExp(loc, type), e); +} + +/***************************************** + * Lower any aggregate that is not an array to an array using a + * regular foreach loop within CTFE. If there are multiple + * `static foreach` loop variables, an array of tuples is + * generated. In thise case, the field `needExpansion` is set to + * true to indicate that the static foreach loop expansion will + * need to expand the tuples into multiple variables. + * + * For example, `static foreach (x; range) { ... }` is lowered to: + * + * static foreach (x; { + * typeof({ + * foreach (x; range) return x; + * }())[] __res; + * foreach (x; range) __res ~= x; + * return __res; + * }()) { ... } + * + * Finally, call `lowerArrayAggregate` to turn the produced + * array into an expression tuple. + * + * Params: + * sc = The current scope. + */ + +static void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc) +{ + size_t nvars = sfe->aggrfe ? sfe->aggrfe->parameters->dim : 1; + Loc aloc = sfe->aggrfe ? sfe->aggrfe->aggr->loc : sfe->rangefe->lwr->loc; + // We need three sets of foreach loop variables because the + // lowering contains three foreach loops. + Parameters *pparams[3] = {new Parameters(), new Parameters(), new Parameters()}; + for (size_t i = 0; i < nvars; i++) + { + for (size_t j = 0; j < 3; j++) + { + Parameters *params = pparams[j]; + Parameter *p = sfe->aggrfe ? (*sfe->aggrfe->parameters)[i] : sfe->rangefe->prm; + params->push(new Parameter(p->storageClass, p->type, p->ident, NULL)); + } + } + Expression *res[2]; + TypeStruct *tplty = NULL; + if (nvars == 1) // only one `static foreach` variable, generate identifiers. + { + for (size_t i = 0; i < 2; i++) + { + res[i] = new IdentifierExp(aloc, (*pparams[i])[0]->ident); + } + } + else // multiple `static foreach` variables, generate tuples. + { + for (size_t i = 0; i < 2; i++) + { + Expressions *e = new Expressions(); + for (size_t j = 0; j < pparams[0]->dim; j++) + { + Parameter *p = (*pparams[i])[j]; + e->push(new IdentifierExp(aloc, p->ident)); + } + if (!tplty) + { + tplty = createTupleType(aloc, e); + } + res[i] = createTuple(aloc, tplty, e); + } + sfe->needExpansion = true; // need to expand the tuples later + } + // generate remaining code for the new aggregate which is an + // array (see documentation comment). + if (sfe->rangefe) + { + sc = sc->startCTFE(); + sfe->rangefe->lwr = semantic(sfe->rangefe->lwr, sc); + sfe->rangefe->lwr = resolveProperties(sc, sfe->rangefe->lwr); + sfe->rangefe->upr = semantic(sfe->rangefe->upr, sc); + sfe->rangefe->upr = resolveProperties(sc, sfe->rangefe->upr); + sc = sc->endCTFE(); + sfe->rangefe->lwr = sfe->rangefe->lwr->optimize(WANTvalue); + sfe->rangefe->lwr = sfe->rangefe->lwr->ctfeInterpret(); + sfe->rangefe->upr = sfe->rangefe->upr->optimize(WANTvalue); + sfe->rangefe->upr = sfe->rangefe->upr->ctfeInterpret(); + } + Statements *s1 = new Statements(); + Statements *sfebody = new Statements(); + if (tplty) sfebody->push(new ExpStatement(sfe->loc, tplty->sym)); + sfebody->push(new ReturnStatement(aloc, res[0])); + s1->push(createForeach(sfe, aloc, pparams[0], new CompoundStatement(aloc, sfebody))); + s1->push(new ExpStatement(aloc, new AssertExp(aloc, new IntegerExp(aloc, 0, Type::tint32)))); + Type *ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1))); + Type *aty = ety->arrayOf(); + Identifier *idres = Identifier::generateId("__res"); + VarDeclaration *vard = new VarDeclaration(aloc, aty, idres, NULL); + Statements *s2 = new Statements(); + s2->push(new ExpStatement(aloc, vard)); + Expression *catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); + s2->push(createForeach(sfe, aloc, pparams[1], new ExpStatement(aloc, catass))); + s2->push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); + Expression *aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2)); + sc = sc->startCTFE(); + aggr = semantic(aggr, sc); + aggr = resolveProperties(sc, aggr); + sc = sc->endCTFE(); + aggr = aggr->optimize(WANTvalue); + aggr = aggr->ctfeInterpret(); + + assert(!!sfe->aggrfe ^ !!sfe->rangefe); + sfe->aggrfe = new ForeachStatement(sfe->loc, TOKforeach, pparams[2], aggr, + sfe->aggrfe ? sfe->aggrfe->_body : sfe->rangefe->_body, + sfe->aggrfe ? sfe->aggrfe->endloc : sfe->rangefe->endloc); + sfe->rangefe = NULL; + lowerArrayAggregate(sfe, sc); // finally, turn generated array into expression tuple +} + +/***************************************** + * Perform `static foreach` lowerings that are necessary in order + * to finally expand the `static foreach` using + * `ddmd.statementsem.makeTupleForeach`. + */ + +void staticForeachPrepare(StaticForeach *sfe, Scope *sc) +{ + assert(sc); + if (sfe->aggrfe) + { + sc = sc->startCTFE(); + sfe->aggrfe->aggr = semantic(sfe->aggrfe->aggr, sc); + sc = sc->endCTFE(); + sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue); + Type *tab = sfe->aggrfe->aggr->type->toBasetype(); + if (tab->ty != Ttuple) + { + sfe->aggrfe->aggr = sfe->aggrfe->aggr->ctfeInterpret(); + } + } + + if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Terror) + { + return; + } + + if (!staticForeachReady(sfe)) + { + if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Tarray) + { + lowerArrayAggregate(sfe, sc); + } + else + { + lowerNonArrayAggregate(sfe, sc); + } + } +} + +/***************************************** + * Returns: + * `true` iff ready to call `ddmd.statementsem.makeTupleForeach`. + */ + +bool staticForeachReady(StaticForeach *sfe) +{ + return sfe->aggrfe && sfe->aggrfe->aggr && sfe->aggrfe->aggr->type && + sfe->aggrfe->aggr->type->toBasetype()->ty == Ttuple; +} + +/* ============================================================ */ + DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident) : Condition(Loc()) { @@ -324,7 +647,6 @@ StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp) : Condition(loc) { this->exp = exp; - this->nest = 0; } Condition *StaticIfCondition::syntaxCopy() @@ -336,13 +658,6 @@ int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds) { if (inc == 0) { - if (exp->op == TOKerror || nest > 100) - { - error(loc, (nest > 1000) ? "unresolvable circular static if expression" - : "error evaluating static if expression"); - goto Lerror; - } - if (!sc) { error(loc, "static if conditional cannot be at global scope"); @@ -350,14 +665,12 @@ int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds) return 0; } - ++nest; sc = sc->push(sc->scopesym); sc->sds = sds; // sds gets any addMember() bool errors = false; bool result = evalStaticCondition(sc, exp, exp, errors); sc->pop(); - --nest; // Prevent repeated condition evaluation. // See: fail_compilation/fail7815.d diff --git a/gcc/d/dmd/cond.h b/gcc/d/dmd/cond.h index 8e33b16a9da..576de8c26c2 100644 --- a/gcc/d/dmd/cond.h +++ b/gcc/d/dmd/cond.h @@ -53,9 +53,13 @@ public: bool needExpansion; + StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe); StaticForeach *syntaxCopy(); }; +void staticForeachPrepare(StaticForeach *sfe, Scope *sc); +bool staticForeachReady(StaticForeach *sfe); + class DVCondition : public Condition { public: @@ -100,7 +104,6 @@ class StaticIfCondition : public Condition { public: Expression *exp; - int nest; // limit circular dependencies StaticIfCondition(Loc loc, Expression *exp); Condition *syntaxCopy(); diff --git a/gcc/d/dmd/cppmangle.c b/gcc/d/dmd/cppmangle.c index 9b24fd2c2e4..6179bfd789e 100644 --- a/gcc/d/dmd/cppmangle.c +++ b/gcc/d/dmd/cppmangle.c @@ -261,7 +261,7 @@ class CppMangleVisitor : public Visitor fatal(); } } - else if(tp->isTemplateThisParameter()) + else if (tp->isTemplateThisParameter()) { ti->error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o->toChars()); fatal(); diff --git a/gcc/d/dmd/declaration.c b/gcc/d/dmd/declaration.c index 0018d9501f0..806e29d6907 100644 --- a/gcc/d/dmd/declaration.c +++ b/gcc/d/dmd/declaration.c @@ -340,6 +340,9 @@ void AliasDeclaration::semantic(Scope *sc) void AliasDeclaration::aliasSemantic(Scope *sc) { //printf("AliasDeclaration::semantic() %s\n", toChars()); + // TypeTraits needs to know if it's located in an AliasDeclaration + sc->flags |= SCOPEalias; + if (aliassym) { FuncDeclaration *fd = aliassym->isFuncLiteralDeclaration(); @@ -347,7 +350,10 @@ void AliasDeclaration::aliasSemantic(Scope *sc) if (fd || (td && td->literal)) { if (fd && fd->semanticRun >= PASSsemanticdone) + { + sc->flags &= ~SCOPEalias; return; + } Expression *e = new FuncExp(loc, aliassym); e = ::semantic(e, sc); @@ -361,11 +367,13 @@ void AliasDeclaration::aliasSemantic(Scope *sc) aliassym = NULL; type = Type::terror; } + sc->flags &= ~SCOPEalias; return; } if (aliassym->isTemplateInstance()) aliassym->semantic(sc); + sc->flags &= ~SCOPEalias; return; } inuse = 1; @@ -470,6 +478,7 @@ void AliasDeclaration::aliasSemantic(Scope *sc) if (!overloadInsert(sx)) ScopeDsymbol::multiplyDefined(Loc(), sx, this); } + sc->flags &= ~SCOPEalias; } bool AliasDeclaration::overloadInsert(Dsymbol *s) diff --git a/gcc/d/dmd/dinterpret.c b/gcc/d/dmd/dinterpret.c index a1658bbd051..61f5cdb0730 100644 --- a/gcc/d/dmd/dinterpret.c +++ b/gcc/d/dmd/dinterpret.c @@ -4649,6 +4649,10 @@ public: result = getVarExp(e->loc, istate, ((SymbolExp *)ea)->var, ctfeNeedRvalue); else if (ea->op == TOKaddress) result = interpret(((AddrExp *)ea)->e1, istate); + // https://issues.dlang.org/show_bug.cgi?id=18871 + // https://issues.dlang.org/show_bug.cgi?id=18819 + else if (ea->op == TOKarrayliteral) + result = interpret((ArrayLiteralExp *)ea, istate); else assert(0); if (CTFEExp::isCantExp(result)) diff --git a/gcc/d/dmd/dmangle.c b/gcc/d/dmd/dmangle.c index 44f4f826b41..f41f6284dc5 100644 --- a/gcc/d/dmd/dmangle.c +++ b/gcc/d/dmd/dmangle.c @@ -80,6 +80,7 @@ void initTypeMangle() mangleChar[Tslice] = "@"; mangleChar[Treturn] = "@"; mangleChar[Tvector] = "@"; + mangleChar[Ttraits] = "@"; mangleChar[Tnull] = "n"; // same as TypeNone diff --git a/gcc/d/dmd/dsymbol.c b/gcc/d/dmd/dsymbol.c index 9aec87a04f5..05ab04c8989 100644 --- a/gcc/d/dmd/dsymbol.c +++ b/gcc/d/dmd/dsymbol.c @@ -321,12 +321,12 @@ Dsymbol *Dsymbol::toAlias2() */ Dsymbol *Dsymbol::pastMixin() { - Dsymbol *s = this; - //printf("Dsymbol::pastMixin() %s\n", toChars()); - while (s && s->isTemplateMixin()) - s = s->parent; - return s; + if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) + return this; + if (!parent) + return NULL; + return parent->pastMixin(); } /// ditto @@ -334,7 +334,8 @@ Dsymbol *Dsymbol::pastMixinAndNspace() { //printf("Dsymbol::pastMixinAndNspace() %s\n", toChars()); Nspace *ns = isNspace(); - if (!(ns && ns->mangleOnly) && !isTemplateMixin() && !isForwardingAttribDeclaration()) + if (!(ns && ns->mangleOnly) && + !isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) return this; if (!parent) return NULL; @@ -382,10 +383,12 @@ Dsymbol *Dsymbol::toParent() /// ditto Dsymbol *Dsymbol::toParent2() { - Dsymbol *s = parent; - while (s && s->isTemplateInstance()) - s = s->parent; - return s; + if (!parent || + (!parent->isTemplateInstance() && + !parent->isForwardingAttribDeclaration() && + !parent->isForwardingScopeDsymbol())) + return parent; + return parent->toParent2(); } /// ditto @@ -951,6 +954,83 @@ const char *OverloadSet::kind() const } +/********************************* ForwardingScopeDsymbol ******************/ + +ForwardingScopeDsymbol::ForwardingScopeDsymbol(ScopeDsymbol *forward) + : ScopeDsymbol() +{ + this->forward = forward; +} + +Dsymbol *ForwardingScopeDsymbol::symtabInsert(Dsymbol *s) +{ + assert(forward); + if (Declaration *d = s->isDeclaration()) + { + if (d->storage_class & STClocal) + { + // Symbols with storage class STClocal are not + // forwarded, but stored in the local symbol + // table. (Those are the `static foreach` variables.) + if (!symtab) + { + symtab = new DsymbolTable(); + } + return ScopeDsymbol::symtabInsert(s); // insert locally + } + } + if (!forward->symtab) + { + forward->symtab = new DsymbolTable(); + } + // Non-STClocal symbols are forwarded to `forward`. + return forward->symtabInsert(s); +} + +/************************ + * This override handles the following two cases: + * static foreach (i, i; [0]) { ... } + * and + * static foreach (i; [0]) { enum i = 2; } + */ +Dsymbol *ForwardingScopeDsymbol::symtabLookup(Dsymbol *s, Identifier *id) +{ + assert(forward); + // correctly diagnose clashing foreach loop variables. + if (Declaration *d = s->isDeclaration()) + { + if (d->storage_class & STClocal) + { + if (!symtab) + { + symtab = new DsymbolTable(); + } + return ScopeDsymbol::symtabLookup(s,id); + } + } + // Declarations within `static foreach` do not clash with + // `static foreach` loop variables. + if (!forward->symtab) + { + forward->symtab = new DsymbolTable(); + } + return forward->symtabLookup(s,id); +} + +void ForwardingScopeDsymbol::importScope(Dsymbol *s, Prot protection) +{ + forward->importScope(s, protection); +} + +void ForwardingScopeDsymbol::semantic(Scope *) +{ +} + +const char *ForwardingScopeDsymbol::kind() const +{ + return "local scope"; +} + /********************************* ScopeDsymbol ****************************/ ScopeDsymbol::ScopeDsymbol() diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index a840261c0bf..788b67e7b74 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -376,8 +376,10 @@ public: class ForwardingScopeDsymbol : public ScopeDsymbol { +public: ScopeDsymbol *forward; + ForwardingScopeDsymbol(ScopeDsymbol *forward); Dsymbol *symtabInsert(Dsymbol *s); Dsymbol *symtabLookup(Dsymbol *s, Identifier *id); void importScope(Dsymbol *s, Prot protection); diff --git a/gcc/d/dmd/expression.c b/gcc/d/dmd/expression.c index 5f1bfa8f5a9..ccfb4b69a29 100644 --- a/gcc/d/dmd/expression.c +++ b/gcc/d/dmd/expression.c @@ -2185,6 +2185,11 @@ StringExp *Expression::toStringExp() return NULL; } +TupleExp *Expression::toTupleExp() +{ + return NULL; +} + /*************************************** * Return !=0 if expression is an lvalue. */ @@ -4542,6 +4547,11 @@ Expression *TupleExp::syntaxCopy() return new TupleExp(loc, e0 ? e0->syntaxCopy() : NULL, arraySyntaxCopy(exps)); } +TupleExp *TupleExp::toTupleExp() +{ + return this; +} + /******************************** FuncExp *********************************/ FuncExp::FuncExp(Loc loc, Dsymbol *s) diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index b460e8caa01..60448600e24 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -157,6 +157,7 @@ public: virtual real_t toImaginary(); virtual complex_t toComplex(); virtual StringExp *toStringExp(); + virtual TupleExp *toTupleExp(); virtual bool isLvalue(); virtual Expression *toLvalue(Scope *sc, Expression *e); virtual Expression *modifiableLvalue(Scope *sc, Expression *e); @@ -397,6 +398,7 @@ public: TupleExp(Loc loc, Expression *e0, Expressions *exps); TupleExp(Loc loc, Expressions *exps); TupleExp(Loc loc, TupleDeclaration *tup); + TupleExp *toTupleExp(); Expression *syntaxCopy(); bool equals(RootObject *o); diff --git a/gcc/d/dmd/expressionsem.c b/gcc/d/dmd/expressionsem.c index c23e332b180..781bd3ea5fd 100644 --- a/gcc/d/dmd/expressionsem.c +++ b/gcc/d/dmd/expressionsem.c @@ -1758,15 +1758,30 @@ public: else { // Disallow shadowing - for (Scope *scx = sc->enclosing; scx && scx->func == sc->func; scx = scx->enclosing) + for (Scope *scx = sc->enclosing; scx && (scx->func == sc->func || (scx->func && sc->func->fes)); scx = scx->enclosing) { Dsymbol *s2; if (scx->scopesym && scx->scopesym->symtab && (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && s != s2) { - e->error("%s %s is shadowing %s %s", s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars()); - return setError(); + // allow STClocal symbols to be shadowed + // TODO: not reallly an optimal design + Declaration *decl = s2->isDeclaration(); + if (!decl || !(decl->storage_class & STClocal)) + { + if (sc->func->fes) + { + e->deprecation("%s `%s` is shadowing %s `%s`. Rename the `foreach` variable.", + s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars()); + } + else + { + e->error("%s %s is shadowing %s %s", + s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars()); + return setError(); + } + } } } } @@ -7930,6 +7945,12 @@ public: if (f1 || f2) return setError(); + if (exp->e1->op == TOKtype || exp->e2->op == TOKtype) + { + result = exp->incompatibleTypes(); + return; + } + exp->type = Type::tbool; if (exp->e1->type != exp->e2->type && exp->e1->type->isfloating() && exp->e2->type->isfloating()) diff --git a/gcc/d/dmd/func.c b/gcc/d/dmd/func.c index 11e4b2f721b..ab74dc5328b 100644 --- a/gcc/d/dmd/func.c +++ b/gcc/d/dmd/func.c @@ -1491,8 +1491,7 @@ void FuncDeclaration::semantic3(Scope *sc) * e.g. * class C { int x; static assert(is(typeof({ this.x = 1; }))); } * - * To properly accept it, mark these lambdas as member functions - - * isThis() returns true and isNested() returns false. + * To properly accept it, mark these lambdas as member functions. */ if (FuncLiteralDeclaration *fld = isFuncLiteralDeclaration()) { @@ -1510,7 +1509,6 @@ void FuncDeclaration::semantic3(Scope *sc) if (fld->tok != TOKfunction) fld->tok = TOKdelegate; } - assert(!isNested()); } } diff --git a/gcc/d/dmd/hdrgen.c b/gcc/d/dmd/hdrgen.c index 395aa3212b5..2436f6eba8f 100644 --- a/gcc/d/dmd/hdrgen.c +++ b/gcc/d/dmd/hdrgen.c @@ -249,7 +249,7 @@ public: buf->writenl(); } - void visit(ForeachStatement *s) + void foreachWithoutBody(ForeachStatement *s) { buf->writestring(Token::toChars(s->op)); buf->writestring(" ("); @@ -269,6 +269,11 @@ public: s->aggr->accept(this); buf->writeByte(')'); buf->writenl(); + } + + void visit(ForeachStatement *s) + { + foreachWithoutBody(s); buf->writeByte('{'); buf->writenl(); buf->level++; @@ -279,7 +284,7 @@ public: buf->writenl(); } - void visit(ForeachRangeStatement *s) + void foreachRangeWithoutBody(ForeachRangeStatement *s) { buf->writestring(Token::toChars(s->op)); buf->writestring(" ("); @@ -297,6 +302,11 @@ public: buf->writenl(); buf->writeByte('{'); buf->writenl(); + } + + void visit(ForeachRangeStatement *s) + { + foreachRangeWithoutBody(s); buf->level++; if (s->_body) s->_body->accept(this); @@ -305,6 +315,20 @@ public: buf->writenl(); } + void visit(StaticForeachStatement *s) + { + buf->writestring("static "); + if (s->sfe->aggrfe) + { + visit(s->sfe->aggrfe); + } + else + { + assert(s->sfe->rangefe); + visit(s->sfe->rangefe); + } + } + void visit(IfStatement *s) { buf->writestring("if ("); @@ -767,6 +791,12 @@ public: buf->writestring(t->dstring); } + void visit(TypeTraits *t) + { + //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod); + t->exp->accept(this); + } + void visit(TypeVector *t) { //printf("TypeVector::toCBuffer2(t->mod = %d)\n", t->mod); @@ -1360,6 +1390,32 @@ public: buf->writenl(); } + void visit(ForwardingStatement *s) + { + s->statement->accept(this); + } + + void visit(StaticForeachDeclaration *s) + { + buf->writestring("static "); + if (s->sfe->aggrfe) + { + foreachWithoutBody(s->sfe->aggrfe); + } + else + { + assert(s->sfe->rangefe); + foreachRangeWithoutBody(s->sfe->rangefe); + } + buf->writeByte('{'); + buf->writenl(); + buf->level++; + visit((AttribDeclaration *)s); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + void visit(CompileDeclaration *d) { buf->writestring("mixin("); @@ -1787,6 +1843,8 @@ public: void visit(AliasDeclaration *d) { + if (d->storage_class & STClocal) + return; buf->writestring("alias "); if (d->aliassym) { @@ -1818,6 +1876,8 @@ public: void visit(VarDeclaration *d) { + if (d->storage_class & STClocal) + return; visitVarDecl(d, false); buf->writeByte(';'); buf->writenl(); @@ -2653,7 +2713,8 @@ public: void visit(TraitsExp *e) { buf->writestring("__traits("); - buf->writestring(e->ident->toChars()); + if (e->ident) + buf->writestring(e->ident->toChars()); if (e->args) { for (size_t i = 0; i < e->args->dim; i++) @@ -3241,6 +3302,7 @@ const char *stcToChars(StorageClass& stc) { STCsystem, TOKat, "@system" }, { STCdisable, TOKat, "@disable" }, { STCfuture, TOKat, "@__future" }, + { STClocal, TOKat, "__local" }, { 0, TOKreserved, NULL } }; diff --git a/gcc/d/dmd/init.c b/gcc/d/dmd/init.c index b40ebe3d953..7bd44ab1fc2 100644 --- a/gcc/d/dmd/init.c +++ b/gcc/d/dmd/init.c @@ -238,7 +238,7 @@ bool hasNonConstPointers(Expression *e) return arrayHasNonConstPointers(ae->keys); return false; } - if(e->op == TOKaddress) + if (e->op == TOKaddress) { AddrExp *ae = (AddrExp *)e; if (ae->e1->op == TOKstructliteral) diff --git a/gcc/d/dmd/intrange.c b/gcc/d/dmd/intrange.c index e0e2472b502..c56d7ba8a93 100644 --- a/gcc/d/dmd/intrange.c +++ b/gcc/d/dmd/intrange.c @@ -610,7 +610,7 @@ IntRange IntRange::operator/(const IntRange& rhs) const { r.imax.value--; } - else if(r.imin.value == 0) + else if (r.imin.value == 0) { r.imin.value++; } diff --git a/gcc/d/dmd/json.c b/gcc/d/dmd/json.c index acdafa530d2..fa49e9240e2 100644 --- a/gcc/d/dmd/json.c +++ b/gcc/d/dmd/json.c @@ -454,6 +454,8 @@ public: void jsonProperties(Declaration *d) { + if (d->storage_class & STClocal) + return; jsonProperties((Dsymbol *)d); propertyStorageClass("storageClass", d->storage_class); @@ -843,6 +845,8 @@ public: void visit(VarDeclaration *d) { + if (d->storage_class & STClocal) + return; objectStart(); jsonProperties(d); diff --git a/gcc/d/dmd/mtype.c b/gcc/d/dmd/mtype.c index b76b5baad25..aa1880624ce 100644 --- a/gcc/d/dmd/mtype.c +++ b/gcc/d/dmd/mtype.c @@ -202,6 +202,7 @@ void Type::_init() sizeTy[Terror] = sizeof(TypeError); sizeTy[Tnull] = sizeof(TypeNull); sizeTy[Tvector] = sizeof(TypeVector); + sizeTy[Ttraits] = sizeof(TypeTraits); initTypeMangle(); @@ -6459,7 +6460,7 @@ Type *TypeDelegate::addStorageClass(StorageClass stc) * alias dg_t = void* delegate(); * scope dg_t dg = ...; */ - if(stc & STCscope) + if (stc & STCscope) { Type *n = t->next->addStorageClass(STCscope | STCscopeinferred); if (n != t->next) @@ -6554,7 +6555,156 @@ bool TypeDelegate::hasPointers() return true; } +/***************************** TypeTraits ********************************/ + +TypeTraits::TypeTraits(const Loc &loc, TraitsExp *exp) + : Type(Ttraits) +{ + this->loc = loc; + this->exp = exp; + this->sym = NULL; +} + +Type *TypeTraits::syntaxCopy() +{ + TraitsExp *te = (TraitsExp *) exp->syntaxCopy(); + TypeTraits *tt = new TypeTraits(loc, te); + tt->mod = mod; + return tt; +} +Type *TypeTraits::semantic(Loc, Scope *sc) +{ + if (ty == Terror) + return this; + + const int inAlias = (sc->flags & SCOPEalias) != 0; + if (exp->ident != Id::allMembers && + exp->ident != Id::derivedMembers && + exp->ident != Id::getMember && + exp->ident != Id::parent && + exp->ident != Id::getOverloads && + exp->ident != Id::getVirtualFunctions && + exp->ident != Id::getVirtualMethods && + exp->ident != Id::getAttributes && + exp->ident != Id::getUnitTests && + exp->ident != Id::getAliasThis) + { + static const char *ctxt[2] = {"as type", "in alias"}; + ::error(loc, "trait `%s` is either invalid or not supported %s", + exp->ident->toChars(), ctxt[inAlias]); + ty = Terror; + return this; + } + + Type *result = NULL; + + if (Expression *e = semanticTraits(exp, sc)) + { + switch (e->op) + { + case TOKdotvar: + sym = ((DotVarExp *)e)->var; + break; + case TOKvar: + sym = ((VarExp *)e)->var; + break; + case TOKfunction: + { + FuncExp *fe = (FuncExp *)e; + if (fe->td) + sym = fe->td; + else + sym = fe->fd; + break; + } + case TOKdottd: + sym = ((DotTemplateExp*)e)->td; + break; + case TOKdsymbol: + sym = ((DsymbolExp *)e)->s; + break; + case TOKtemplate: + sym = ((TemplateExp *)e)->td; + break; + case TOKscope: + sym = ((ScopeExp *)e)->sds; + break; + case TOKtuple: + { + TupleExp *te = e->toTupleExp(); + Objects *elems = new Objects; + elems->setDim(te->exps->dim); + for (size_t i = 0; i < elems->dim; i++) + { + Expression *src = (*te->exps)[i]; + switch (src->op) + { + case TOKtype: + (*elems)[i] = ((TypeExp *)src)->type; + break; + case TOKdottype: + (*elems)[i] = ((DotTypeExp *)src)->type; + break; + case TOKoverloadset: + (*elems)[i] = ((OverExp *)src)->type; + break; + default: + if (Dsymbol *sym = isDsymbol(src)) + (*elems)[i] = sym; + else + (*elems)[i] = src; + } + } + TupleDeclaration *td = new TupleDeclaration(e->loc, + Identifier::generateId("__aliastup"), elems); + sym = td; + break; + } + case TOKdottype: + result = isType(((DotTypeExp *)e)->sym); + break; + case TOKtype: + result = ((TypeExp *)e)->type; + break; + case TOKoverloadset: + result = ((OverExp *)e)->type; + break; + default: + break; + } + } + + if (result) + result = result->addMod(mod); + if (!inAlias && !result) + { + if (!global.errors) + ::error(loc, "`%s` does not give a valid type", toChars()); + return Type::terror; + } + + return result; +} + +void TypeTraits::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool) +{ + *pt = NULL; + *pe = NULL; + *ps = NULL; + + if (Type *t = semantic(loc, sc)) + *pt = t; + else if (sym) + *ps = sym; + else + *pt = Type::terror; +} + +d_uns64 TypeTraits::size(Loc) +{ + return SIZE_INVALID; +} /***************************** TypeQualified *****************************/ diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index aab0d034cf0..22fabf585ea 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -94,6 +94,7 @@ enum ENUMTY Tvector, Tint128, Tuns128, + Ttraits, TMAX }; typedef unsigned char TY; // ENUMTY @@ -659,6 +660,23 @@ public: void accept(Visitor *v) { v->visit(this); } }; +class TypeTraits : public Type +{ +public: + Loc loc; + /// The expression to resolve as type or symbol. + TraitsExp *exp; + /// The symbol when exp doesn't represent a type. + Dsymbol *sym; + + TypeTraits(const Loc &loc, TraitsExp *exp); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); + d_uns64 size(Loc loc); + void accept(Visitor *v) { v->visit(this); } +}; + class TypeQualified : public Type { public: diff --git a/gcc/d/dmd/parse.c b/gcc/d/dmd/parse.c index 9da58af046d..b66bddb8ef8 100644 --- a/gcc/d/dmd/parse.c +++ b/gcc/d/dmd/parse.c @@ -351,6 +351,7 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes case TOKunion: case TOKclass: case TOKinterface: + case TOKtraits: Ldeclaration: a = parseDeclarations(false, pAttrs, pAttrs->comment); if (a && a->dim) @@ -485,6 +486,10 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes a = parseImport(); // keep pLastDecl } + else if (next == TOKforeach || next == TOKforeach_reverse) + { + s = parseForeachStaticDecl(token.loc, pLastDecl); + } else { stc = STCstatic; @@ -3144,6 +3149,18 @@ Type *Parser::parseBasicType(bool dontLookDotIdents) t = parseVector(); break; + case TOKtraits: + if (TraitsExp *te = (TraitsExp *) parsePrimaryExp()) + { + if (te->ident && te->args) + { + t = new TypeTraits(token.loc, te); + break; + } + } + t = new TypeError(); + break; + case TOKconst: // const(type) nextToken(); @@ -4699,6 +4716,161 @@ void Parser::checkCstyleTypeSyntax(Loc loc, Type *t, int alt, Identifier *ident) } +/***************************************** + * Parses `foreach` statements, `static foreach` statements and + * `static foreach` declarations. The template parameter + * `isStatic` is true, iff a `static foreach` should be parsed. + * If `isStatic` is true, `isDecl` can be true to indicate that a + * `static foreach` declaration should be parsed. + */ +Statement *Parser::parseForeach(Loc loc, bool *isRange, bool isDecl) +{ + TOK op = token.value; + + nextToken(); + check(TOKlparen); + + Parameters *parameters = new Parameters(); + + while (1) + { + Identifier *ai = NULL; + Type *at; + + StorageClass storageClass = 0; + StorageClass stc = 0; + Lagain: + if (stc) + { + storageClass = appendStorageClass(storageClass, stc); + nextToken(); + } + switch (token.value) + { + case TOKref: + stc = STCref; + goto Lagain; + + case TOKenum: + stc = STCmanifest; + goto Lagain; + + case TOKalias: + storageClass = appendStorageClass(storageClass, STCalias); + nextToken(); + break; + + case TOKconst: + if (peekNext() != TOKlparen) + { + stc = STCconst; + goto Lagain; + } + break; + + case TOKimmutable: + if (peekNext() != TOKlparen) + { + stc = STCimmutable; + goto Lagain; + } + break; + + case TOKshared: + if (peekNext() != TOKlparen) + { + stc = STCshared; + goto Lagain; + } + break; + + case TOKwild: + if (peekNext() != TOKlparen) + { + stc = STCwild; + goto Lagain; + } + break; + + default: + break; + } + if (token.value == TOKidentifier) + { + Token *t = peek(&token); + if (t->value == TOKcomma || t->value == TOKsemicolon) + { ai = token.ident; + at = NULL; // infer argument type + nextToken(); + goto Larg; + } + } + at = parseType(&ai); + if (!ai) + error("no identifier for declarator %s", at->toChars()); + Larg: + Parameter *p = new Parameter(storageClass, at, ai, NULL); + parameters->push(p); + if (token.value == TOKcomma) + { nextToken(); + continue; + } + break; + } + check(TOKsemicolon); + + Expression *aggr = parseExpression(); + if (token.value == TOKslice && parameters->dim == 1) + { + Parameter *p = (*parameters)[0]; + delete parameters; + nextToken(); + Expression *upr = parseExpression(); + check(TOKrparen); + Loc endloc; + Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL; + if (isRange) + *isRange = true; + return new ForeachRangeStatement(loc, op, p, aggr, upr, body, endloc); + } + else + { + check(TOKrparen); + Loc endloc; + Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL; + if (isRange) + *isRange = false; + return new ForeachStatement(loc, op, parameters, aggr, body, endloc); + } +} + +Dsymbol *Parser::parseForeachStaticDecl(Loc loc, Dsymbol **pLastDecl) +{ + nextToken(); + + bool isRange = false; + Statement *s = parseForeach(loc, &isRange, true); + + return new StaticForeachDeclaration( + new StaticForeach(loc, isRange ? NULL : (ForeachStatement *)s, + isRange ? (ForeachRangeStatement *)s : NULL), + parseBlock(pLastDecl) + ); +} + +Statement *Parser::parseForeachStatic(Loc loc) +{ + nextToken(); + + bool isRange = false; + Statement *s = parseForeach(loc, &isRange, false); + + return new StaticForeachStatement(loc, + new StaticForeach(loc, isRange ? NULL : (ForeachStatement *)s, + isRange ? (ForeachRangeStatement *)s : NULL) + ); +} + /***************************************** * Input: * flags PSxxxx @@ -4757,6 +4929,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc case TOKdot: case TOKtypeof: case TOKvector: + case TOKtraits: /* Bugzilla 15163: If tokens can be handled as * old C-style declaration or D expression, prefer the latter. */ @@ -4805,7 +4978,6 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc case TOKtypeid: case TOKis: case TOKlbracket: - case TOKtraits: case TOKfile: case TOKfilefullpath: case TOKline: @@ -4834,6 +5006,13 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc cond = parseStaticIfCondition(); goto Lcondition; } + else if (t->value == TOKforeach || t->value == TOKforeach_reverse) + { + s = parseForeachStatic(loc); + if (flags & PSscope) + s = new ScopeStatement(loc, s, token.loc); + break; + } if (t->value == TOKimport) { Dsymbols *imports = parseImport(); @@ -5086,106 +5265,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc case TOKforeach: case TOKforeach_reverse: { - TOK op = token.value; - - nextToken(); - check(TOKlparen); - - Parameters *parameters = new Parameters(); - - while (1) - { - Identifier *ai = NULL; - Type *at; - - StorageClass storageClass = 0; - StorageClass stc = 0; - Lagain: - if (stc) - { - storageClass = appendStorageClass(storageClass, stc); - nextToken(); - } - switch (token.value) - { - case TOKref: - stc = STCref; - goto Lagain; - - case TOKconst: - if (peekNext() != TOKlparen) - { - stc = STCconst; - goto Lagain; - } - break; - case TOKimmutable: - if (peekNext() != TOKlparen) - { - stc = STCimmutable; - goto Lagain; - } - break; - case TOKshared: - if (peekNext() != TOKlparen) - { - stc = STCshared; - goto Lagain; - } - break; - case TOKwild: - if (peekNext() != TOKlparen) - { - stc = STCwild; - goto Lagain; - } - break; - default: - break; - } - if (token.value == TOKidentifier) - { - Token *t = peek(&token); - if (t->value == TOKcomma || t->value == TOKsemicolon) - { ai = token.ident; - at = NULL; // infer argument type - nextToken(); - goto Larg; - } - } - at = parseType(&ai); - if (!ai) - error("no identifier for declarator %s", at->toChars()); - Larg: - Parameter *p = new Parameter(storageClass, at, ai, NULL); - parameters->push(p); - if (token.value == TOKcomma) - { nextToken(); - continue; - } - break; - } - check(TOKsemicolon); - - Expression *aggr = parseExpression(); - if (token.value == TOKslice && parameters->dim == 1) - { - Parameter *p = (*parameters)[0]; - delete parameters; - nextToken(); - Expression *upr = parseExpression(); - check(TOKrparen); - Loc endloc; - Statement *body = parseStatement(0, NULL, &endloc); - s = new ForeachRangeStatement(loc, op, p, aggr, upr, body, endloc); - } - else - { - check(TOKrparen); - Loc endloc; - Statement *body = parseStatement(0, NULL, &endloc); - s = new ForeachStatement(loc, op, parameters, aggr, body, endloc); - } + s = parseForeach(loc, NULL, false); break; } @@ -6001,6 +6081,27 @@ bool Parser::isBasicType(Token **pt) goto Lfalse; goto L3; + case TOKtraits: + { + // __traits(getMember + t = peek(t); + if (t->value != TOKlparen) + goto Lfalse; + Token *lp = t; + t = peek(t); + if (t->value != TOKidentifier || t->ident != Id::getMember) + goto Lfalse; + if (!skipParens(lp, &lp)) + goto Lfalse; + // we are in a lookup for decl VS statement + // so we expect a declarator following __trait if it's a type. + // other usages wont be ambiguous (alias, template instance, type qual, etc.) + if (lp->value != TOKidentifier) + goto Lfalse; + + break; + } + case TOKconst: case TOKimmutable: case TOKshared: @@ -7390,6 +7491,7 @@ Expression *Parser::parseUnaryExp() case TOKfunction: case TOKdelegate: case TOKtypeof: + case TOKtraits: case TOKvector: case TOKfile: case TOKfilefullpath: diff --git a/gcc/d/dmd/parse.h b/gcc/d/dmd/parse.h index 97630dc3c4d..c5ef0b2cdb6 100644 --- a/gcc/d/dmd/parse.h +++ b/gcc/d/dmd/parse.h @@ -120,6 +120,9 @@ public: FuncDeclaration *parseContracts(FuncDeclaration *f); void checkDanglingElse(Loc elseloc); void checkCstyleTypeSyntax(Loc loc, Type *t, int alt, Identifier *ident); + Statement *parseForeach(Loc loc, bool *isRange, bool isDecl); + Dsymbol *parseForeachStaticDecl(Loc loc, Dsymbol **pLastDecl); + Statement *parseForeachStatic(Loc loc); /** endPtr used for documented unittests */ Statement *parseStatement(int flags, const utf8_t** endPtr = NULL, Loc *pEndloc = NULL); Initializer *parseInitializer(); diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index 37a15fc1acc..d34a0e704f7 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -61,9 +61,10 @@ enum PINLINE; #define SCOPEctfe 0x0080 // inside a ctfe-only expression #define SCOPEcompile 0x0100 // inside __traits(compile) #define SCOPEignoresymbolvisibility 0x0200 // ignore symbol visibility (Bugzilla 15907) -#define SCOPEfullinst 0x1000 // fully instantiate templates #define SCOPEfree 0x8000 // is on free list +#define SCOPEfullinst 0x10000 // fully instantiate templates +#define SCOPEalias 0x20000 // inside alias declaration struct Scope { diff --git a/gcc/d/dmd/statement.c b/gcc/d/dmd/statement.c index 450b3f4f594..6c3443cb9fa 100644 --- a/gcc/d/dmd/statement.c +++ b/gcc/d/dmd/statement.c @@ -32,6 +32,7 @@ bool checkEscapeRef(Scope *sc, Expression *e, bool gag); VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); Expression *semantic(Expression *e, Scope *sc); StringExp *semanticString(Scope *sc, Expression *exp, const char *s); +Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion); Identifier *fixupLabelName(Scope *sc, Identifier *ident) { @@ -410,6 +411,7 @@ Statement *toStatement(Dsymbol *s) void visit(ProtDeclaration *d) { result = visitMembers(d->loc, d->decl); } void visit(AlignDeclaration *d) { result = visitMembers(d->loc, d->decl); } void visit(UserAttributeDeclaration *d) { result = visitMembers(d->loc, d->decl); } + void visit(ForwardingAttribDeclaration *d) { result = visitMembers(d->loc, d->decl); } void visit(StaticAssert *) {} void visit(Import *) {} @@ -420,6 +422,12 @@ Statement *toStatement(Dsymbol *s) result = visitMembers(d->loc, d->include(NULL, NULL)); } + void visit(StaticForeachDeclaration *d) + { + assert(d->sfe && !!d->sfe->aggrfe ^ !!d->sfe->rangefe); + result = visitMembers(d->loc, d->include(NULL, NULL)); + } + void visit(CompileDeclaration *d) { result = visitMembers(d->loc, d->include(NULL, NULL)); @@ -682,6 +690,72 @@ bool ScopeStatement::hasContinue() return statement ? statement->hasContinue() : false; } +/******************************** ForwardingStatement **********************/ + +/* Statement whose symbol table contains foreach index variables in a + * local scope and forwards other members to the parent scope. This + * wraps a statement. + * + * Also see: `ddmd.attrib.ForwardingAttribDeclaration` + */ + +ForwardingStatement::ForwardingStatement(Loc loc, ForwardingScopeDsymbol *sym, Statement *s) + : Statement(loc) +{ + this->sym = sym; + assert(s); + this->statement = s; +} + +ForwardingStatement::ForwardingStatement(Loc loc, Statement *s) + : Statement(loc) +{ + this->sym = new ForwardingScopeDsymbol(NULL); + this->sym->symtab = new DsymbolTable(); + assert(s); + this->statement = s; +} + +Statement *ForwardingStatement::syntaxCopy() +{ + return new ForwardingStatement(loc, statement->syntaxCopy()); +} + +/*********************** + * ForwardingStatements are distributed over the flattened + * sequence of statements. This prevents flattening to be + * "blocked" by a ForwardingStatement and is necessary, for + * example, to support generating scope guards with `static + * foreach`: + * + * static foreach(i; 0 .. 10) scope(exit) writeln(i); + * writeln("this is printed first"); + * // then, it prints 10, 9, 8, 7, ... + */ + +Statements *ForwardingStatement::flatten(Scope *sc) +{ + if (!statement) + { + return NULL; + } + sc = sc->push(sym); + Statements *a = statement->flatten(sc); + sc = sc->pop(); + if (!a) + { + return a; + } + Statements *b = new Statements(); + b->setDim(a->dim); + for (size_t i = 0; i < a->dim; i++) + { + Statement *s = (*a)[i]; + (*b)[i] = s ? new ForwardingStatement(s->loc, sym, s) : NULL; + } + return b; +} + /******************************** WhileStatement ***************************/ WhileStatement::WhileStatement(Loc loc, Expression *c, Statement *b, Loc endloc) @@ -935,6 +1009,52 @@ Statements *ConditionalStatement::flatten(Scope *sc) return a; } +/******************************** StaticForeachStatement ********************/ + +/* Static foreach statements, like: + * void main() + * { + * static foreach(i; 0 .. 10) + * { + * pragma(msg, i); + * } + * } + */ + +StaticForeachStatement::StaticForeachStatement(Loc loc, StaticForeach *sfe) + : Statement(loc) +{ + this->sfe = sfe; +} + +Statement *StaticForeachStatement::syntaxCopy() +{ + return new StaticForeachStatement(loc, sfe->syntaxCopy()); +} + +Statements *StaticForeachStatement::flatten(Scope *sc) +{ + staticForeachPrepare(sfe, sc); + if (staticForeachReady(sfe)) + { + Statement *s = makeTupleForeachStatic(sc, sfe->aggrfe, sfe->needExpansion); + Statements *result = s->flatten(sc); + if (result) + { + return result; + } + result = new Statements(); + result->push(s); + return result; + } + else + { + Statements *result = new Statements(); + result->push(new ErrorStatement()); + return result; + } +} + /******************************** PragmaStatement ***************************/ PragmaStatement::PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body) diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index fae0862b4d6..8f69383bb3a 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -232,15 +232,13 @@ public: class ForwardingStatement : public Statement { +public: ForwardingScopeDsymbol *sym; Statement *statement; + ForwardingStatement(Loc loc, ForwardingScopeDsymbol *sym, Statement *s); + ForwardingStatement(Loc loc, Statement *s); Statement *syntaxCopy(); - Statement *getRelatedLabeled(); - bool hasBreak(); - bool hasContinue(); - Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally); - Statement *last(); Statements *flatten(Scope *sc); ForwardingStatement *isForwardingStatement() { return this; } void accept(Visitor *v) { v->visit(this); } @@ -384,6 +382,7 @@ class StaticForeachStatement : public Statement public: StaticForeach *sfe; + StaticForeachStatement(Loc loc, StaticForeach *sfe); Statement *syntaxCopy(); Statements *flatten(Scope *sc); diff --git a/gcc/d/dmd/statementsem.c b/gcc/d/dmd/statementsem.c index cc2b63e2466..26e5950518a 100644 --- a/gcc/d/dmd/statementsem.c +++ b/gcc/d/dmd/statementsem.c @@ -13,6 +13,7 @@ #include "errors.h" #include "statement.h" +#include "attrib.h" #include "expression.h" #include "cond.h" #include "init.h" @@ -303,11 +304,10 @@ public: void visit(ScopeStatement *ss) { - ScopeDsymbol *sym; //printf("ScopeStatement::semantic(sc = %p)\n", sc); if (ss->statement) { - sym = new ScopeDsymbol(); + ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sym->endlinnum = ss->endloc.linnum; sc = sc->push(sym); @@ -348,6 +348,22 @@ public: result = ss; } + void visit(ForwardingStatement *ss) + { + assert(ss->sym); + for (Scope *csc = sc; !ss->sym->forward; csc = csc->enclosing) + { + assert(csc); + ss->sym->forward = csc->scopesym; + } + sc = sc->push(ss->sym); + sc->sbreak = ss; + sc->scontinue = ss; + ss->statement = semantic(ss->statement, sc); + sc = sc->pop(); + result = ss->statement; + } + void visit(WhileStatement *ws) { /* Rewrite as a for(;condition;) loop @@ -478,6 +494,347 @@ public: result = fs; } + /*********************** + * Declares a unrolled `foreach` loop variable or a `static foreach` variable. + * + * Params: + * storageClass = The storage class of the variable. + * type = The declared type of the variable. + * ident = The name of the variable. + * e = The initializer of the variable (i.e. the current element of the looped over aggregate). + * t = The type of the initializer. + * Returns: + * `true` iff the declaration was successful. + */ + bool declareVariable(ForeachStatement *fs, Type *paramtype, TupleExp *te, + bool needExpansion, bool isStatic, Statements *statements, Dsymbols *declarations, + StorageClass storageClass, Type *type, Identifier *ident, Expression *e, Type *t) + { + Loc loc = fs->loc; + if (storageClass & (STCout | STClazy) || + (storageClass & STCref && !te)) + { + fs->error("no storage class for value %s", ident->toChars()); + return false; + } + Declaration *var; + if (e) + { + Type *tb = e->type->toBasetype(); + Dsymbol *ds = NULL; + if (!(storageClass & STCmanifest)) + { + if ((isStatic || tb->ty == Tfunction || tb->ty == Tsarray || storageClass & STCalias) && e->op == TOKvar) + ds = ((VarExp *)e)->var; + else if (e->op == TOKtemplate) + ds = ((TemplateExp *)e)->td; + else if (e->op == TOKscope) + ds = ((ScopeExp *)e)->sds; + else if (e->op == TOKfunction) + { + FuncExp *fe = (FuncExp *)e; + ds = fe->td ? (Dsymbol *)fe->td : fe->fd; + } + } + else if (storageClass & STCalias) + { + fs->error("foreach loop variable cannot be both enum and alias"); + return false; + } + + if (ds) + { + var = new AliasDeclaration(loc, ident, ds); + if (storageClass & STCref) + { + fs->error("symbol %s cannot be ref", ds->toChars()); + return false; + } + if (paramtype) + { + fs->error("cannot specify element type for symbol %s", ds->toChars()); + return false; + } + } + else if (e->op == TOKtype) + { + var = new AliasDeclaration(loc, ident, e->type); + if (paramtype) + { + fs->error("cannot specify element type for type %s", e->type->toChars()); + return false; + } + } + else + { + e = resolveProperties(sc, e); + type = e->type; + if (paramtype) + type = paramtype; + Initializer *ie = new ExpInitializer(Loc(), e); + VarDeclaration *v = new VarDeclaration(loc, type, ident, ie); + if (storageClass & STCref) + v->storage_class |= STCref | STCforeach; + if (isStatic || storageClass & STCmanifest || e->isConst() || + e->op == TOKstring || + e->op == TOKstructliteral || + e->op == TOKarrayliteral) + { + if (v->storage_class & STCref) + { + if (!isStatic || !needExpansion) + { + fs->error("constant value %s cannot be ref", ie->toChars()); + } + else + { + fs->error("constant value %s cannot be ref", ident->toChars()); + } + return false; + } + else + v->storage_class |= STCmanifest; + } + var = v; + } + } + else + { + var = new AliasDeclaration(loc, ident, t); + if (paramtype) + { + fs->error("cannot specify element type for symbol %s", fs->toChars()); + return false; + } + } + if (isStatic) + var->storage_class |= STClocal; + if (statements) + statements->push(new ExpStatement(loc, var)); + else if (declarations) + declarations->push(var); + else + assert(0); + return true; + } + + bool makeTupleForeachBody(ForeachStatement *fs, size_t k, + Type *paramtype, TupleExp *te, TypeTuple *tuple, + bool needExpansion, bool isStatic, bool isDecl, + Statements *statements, Dsymbols *declarations, Dsymbols *dbody) + { + Loc loc = fs->loc; + Expression *e = NULL; + Type *t = NULL; + if (te) + e = (*te->exps)[k]; + else + t = Parameter::getNth(tuple->arguments, k)->type; + Parameter *p = (*fs->parameters)[0]; + Statements *stmts = (isDecl) ? NULL : new Statements(); + Dsymbols *decls = (isDecl) ? new Dsymbols() : NULL; + + size_t dim = fs->parameters->dim; + if (!needExpansion && dim == 2) + { + // Declare key + if (p->storageClass & (STCout | STCref | STClazy)) + { + fs->error("no storage class for key %s", p->ident->toChars()); + return false; + } + if (isStatic) + { + if (!p->type) + { + p->type = Type::tsize_t; + } + } + p->type = p->type->semantic(loc, sc); + TY keyty = p->type->ty; + if (keyty != Tint32 && keyty != Tuns32) + { + if (global.params.isLP64) + { + if (keyty != Tint64 && keyty != Tuns64) + { + fs->error("foreach: key type must be int or uint, long or ulong, not %s", p->type->toChars()); + return false; + } + } + else + { + fs->error("foreach: key type must be int or uint, not %s", p->type->toChars()); + return false; + } + } + Initializer *ie = new ExpInitializer(Loc(), new IntegerExp(k)); + VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, ie); + var->storage_class |= STCmanifest; + if (isStatic) + var->storage_class |= STClocal; + if (!isDecl) + stmts->push(new ExpStatement(loc, var)); + else + decls->push(var); + p = (*fs->parameters)[1]; // value + } + + if (!isStatic || !needExpansion) + { + // Declare value + if (!declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls, + p->storageClass, p->type, p->ident, e, t)) + { + return false; + } + } + else + { + // expand tuples into multiple `static foreach` variables. + assert(e && !t); + Identifier *ident = Identifier::generateId("__value"); + declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls, + 0, e->type, ident, e, NULL); + Identifier *field = Identifier::idPool("tuple"); + Expression *access = new DotIdExp(loc, e, field); + access = semantic(access, sc); + if (!tuple) + return false; + //printf("%s\n", tuple->toChars()); + for (size_t l = 0; l < dim; l++) + { + Parameter *cp = (*fs->parameters)[l]; + Expression *init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type::tsize_t)); + init_ = semantic(init_, sc); + assert(init_->type); + declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls, + p->storageClass, init_->type, cp->ident, init_, NULL); + } + } + Statement *fwdstmt = NULL; + Dsymbol *fwddecl = NULL; + if (!isDecl) + { + if (fs->_body) + stmts->push(fs->_body->syntaxCopy()); + fwdstmt = new CompoundStatement(loc, stmts); + } + else + { + decls->append(Dsymbol::arraySyntaxCopy(dbody)); + } + if (!isStatic) + { + fwdstmt = new ScopeStatement(loc, fwdstmt, fs->endloc); + } + else if (!isDecl) + { + fwdstmt = new ForwardingStatement(loc, fwdstmt); + } + else + { + fwddecl = new ForwardingAttribDeclaration(decls); + } + + if (statements) + statements->push(fwdstmt); + else if (declarations) + declarations->push(fwddecl); + else + assert(0); + return true; + } + + /******************* + * Type check and unroll `foreach` over an expression tuple as well + * as `static foreach` statements and `static foreach` + * declarations. For `static foreach` statements and `static + * foreach` declarations, the visitor interface is used (and the + * result is written into the `result` field.) For `static + * foreach` declarations, the resulting Dsymbols* are returned + * directly. + * + * The unrolled body is wrapped into a + * - UnrolledLoopStatement, for `foreach` over an expression tuple. + * - ForwardingStatement, for `static foreach` statements. + * - ForwardingAttribDeclaration, for `static foreach` declarations. + * + * `static foreach` variables are declared as `STClocal`, such + * that they are inserted into the local symbol tables of the + * forwarding constructs instead of forwarded. For `static + * foreach` with multiple foreach loop variables whose aggregate + * has been lowered into a sequence of tuples, this function + * expands the tuples into multiple `STClocal` `static foreach` + * variables. + */ + bool makeTupleForeach(ForeachStatement *fs, bool needExpansion, bool isStatic, bool isDecl, + Statements *statements, Dsymbols *declarations, Dsymbols *dbody) + { + Loc loc = fs->loc; + size_t dim = fs->parameters->dim; + if (!needExpansion && (dim < 1 || dim > 2)) + { + fs->error("only one (value) or two (key,value) arguments for tuple foreach"); + return false; + } + + Type *paramtype = (*fs->parameters)[dim-1]->type; + if (paramtype) + { + paramtype = paramtype->semantic(loc, sc); + if (paramtype->ty == Terror) + return false; + } + + Type *tab = fs->aggr->type->toBasetype(); + TypeTuple *tuple = (TypeTuple *)tab; + //printf("aggr: op = %d, %s\n", fs->aggr->op, fs->aggr->toChars()); + size_t n; + TupleExp *te = NULL; + if (fs->aggr->op == TOKtuple) // expression tuple + { + te = (TupleExp *)fs->aggr; + n = te->exps->dim; + } + else if (fs->aggr->op == TOKtype) // type tuple + { + n = Parameter::dim(tuple->arguments); + } + else + assert(0); + for (size_t j = 0; j < n; j++) + { + size_t k = (fs->op == TOKforeach) ? j : n - 1 - j; + if (!makeTupleForeachBody(fs, k, paramtype, te, tuple, + needExpansion, isStatic, isDecl, + statements, declarations, dbody)) + return false; + } + return true; + } + + Dsymbols *makeTupleForeachStaticDecl(ForeachStatement *fs, Dsymbols *dbody, bool needExpansion) + { + assert(sc); + Dsymbols *declarations = new Dsymbols(); + if (!makeTupleForeach(fs, needExpansion, true, true, NULL, declarations, dbody)) + return NULL; + + return declarations; + } + + void makeTupleForeachStatic(ForeachStatement *fs, bool needExpansion) + { + Loc loc = fs->loc; + assert(sc); + Statements *statements = new Statements(); + if (!makeTupleForeach(fs, needExpansion, true, false, statements, NULL, NULL)) + return setError(); + + result = new CompoundStatement(loc, statements); + } + void visit(ForeachStatement *fs) { //printf("ForeachStatement::semantic() %p\n", fs); @@ -575,177 +932,22 @@ public: if (tab->ty == Ttuple) // don't generate new scope for tuple loops { - if (dim < 1 || dim > 2) - { - fs->error("only one (value) or two (key,value) arguments for tuple foreach"); + Statements *statements = new Statements(); + if (!makeTupleForeach(fs, false, false, false, statements, NULL, NULL)) return setError(); - } - Type *paramtype = (*fs->parameters)[dim-1]->type; - if (paramtype) - { - paramtype = paramtype->semantic(loc, sc); - if (paramtype->ty == Terror) - return setError(); - } - - TypeTuple *tuple = (TypeTuple *)tab; - Statements *statements = new Statements(); - //printf("aggr: op = %d, %s\n", fs->aggr->op, fs->aggr->toChars()); - size_t n; - TupleExp *te = NULL; - if (fs->aggr->op == TOKtuple) // expression tuple - { - te = (TupleExp *)fs->aggr; - n = te->exps->dim; - } - else if (fs->aggr->op == TOKtype) // type tuple + result = new UnrolledLoopStatement(loc, statements); + if (LabelStatement *ls = checkLabeledLoop(sc, fs)) + ls->gotoTarget = result; + if (fs->aggr->op == TOKtuple) { - n = Parameter::dim(tuple->arguments); + TupleExp *te = (TupleExp *)fs->aggr; + if (te->e0) + result = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), result); } - else - assert(0); - for (size_t j = 0; j < n; j++) - { - size_t k = (fs->op == TOKforeach) ? j : n - 1 - j; - Expression *e = NULL; - Type *t = NULL; - if (te) - e = (*te->exps)[k]; - else - t = Parameter::getNth(tuple->arguments, k)->type; - Parameter *p = (*fs->parameters)[0]; - Statements *st = new Statements(); - - if (dim == 2) - { - // Declare key - if (p->storageClass & (STCout | STCref | STClazy)) - { - fs->error("no storage class for key %s", p->ident->toChars()); - return setError(); - } - p->type = p->type->semantic(loc, sc); - TY keyty = p->type->ty; - if (keyty != Tint32 && keyty != Tuns32) - { - if (global.params.isLP64) - { - if (keyty != Tint64 && keyty != Tuns64) - { - fs->error("foreach: key type must be int or uint, long or ulong, not %s", p->type->toChars()); - return setError(); - } - } - else - { - fs->error("foreach: key type must be int or uint, not %s", p->type->toChars()); - return setError(); - } - } - Initializer *ie = new ExpInitializer(Loc(), new IntegerExp(k)); - VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, ie); - var->storage_class |= STCmanifest; - st->push(new ExpStatement(loc, var)); - p = (*fs->parameters)[1]; // value - } - // Declare value - if (p->storageClass & (STCout | STClazy) || - (p->storageClass & STCref && !te)) - { - fs->error("no storage class for value %s", p->ident->toChars()); - return setError(); - } - Dsymbol *var; - if (te) - { - Type *tb = e->type->toBasetype(); - Dsymbol *ds = NULL; - if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar) - ds = ((VarExp *)e)->var; - else if (e->op == TOKtemplate) - ds = ((TemplateExp *)e)->td; - else if (e->op == TOKscope) - ds = ((ScopeExp *)e)->sds; - else if (e->op == TOKfunction) - { - FuncExp *fe = (FuncExp *)e; - ds = fe->td ? (Dsymbol *)fe->td : fe->fd; - } - - if (ds) - { - var = new AliasDeclaration(loc, p->ident, ds); - if (p->storageClass & STCref) - { - fs->error("symbol %s cannot be ref", s->toChars()); - return setError(); - } - if (paramtype) - { - fs->error("cannot specify element type for symbol %s", ds->toChars()); - return setError(); - } - } - else if (e->op == TOKtype) - { - var = new AliasDeclaration(loc, p->ident, e->type); - if (paramtype) - { - fs->error("cannot specify element type for type %s", e->type->toChars()); - return setError(); - } - } - else - { - p->type = e->type; - if (paramtype) - p->type = paramtype; - Initializer *ie = new ExpInitializer(Loc(), e); - VarDeclaration *v = new VarDeclaration(loc, p->type, p->ident, ie); - if (p->storageClass & STCref) - v->storage_class |= STCref | STCforeach; - if (e->isConst() || e->op == TOKstring || - e->op == TOKstructliteral || e->op == TOKarrayliteral) - { - if (v->storage_class & STCref) - { - fs->error("constant value %s cannot be ref", ie->toChars()); - return setError(); - } - else - v->storage_class |= STCmanifest; - } - var = v; - } - } - else - { - var = new AliasDeclaration(loc, p->ident, t); - if (paramtype) - { - fs->error("cannot specify element type for symbol %s", s->toChars()); - return setError(); - } - } - st->push(new ExpStatement(loc, var)); - - if (fs->_body) - st->push(fs->_body->syntaxCopy()); - s = new CompoundStatement(loc, st); - s = new ScopeStatement(loc, s, fs->endloc); - statements->push(s); - } - - s = new UnrolledLoopStatement(loc, statements); - if (LabelStatement *ls = checkLabeledLoop(sc, fs)) - ls->gotoTarget = s; - if (te && te->e0) - s = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), s); if (vinit) - s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s); - s = semantic(s, sc); - result = s; + result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result); + result = semantic(result, sc); return; } @@ -756,6 +958,19 @@ public: sc2->noctor++; + for (size_t i = 0; i < dim; i++) + { + Parameter *p = (*fs->parameters)[i]; + if (p->storageClass & STCmanifest) + { + fs->error("cannot declare enum loop variables for non-unrolled foreach"); + } + if (p->storageClass & STCalias) + { + fs->error("cannot declare alias loop variables for non-unrolled foreach"); + } + } + switch (tab->ty) { case Tarray: @@ -1949,6 +2164,11 @@ public: if (ps->_body) { + if (ps->ident == Id::msg || ps->ident == Id::startaddress) + { + ps->error("`pragma(%s)` is missing a terminating `;`", ps->ident->toChars()); + return setError(); + } ps->_body = semantic(ps->_body, sc); } result = ps->_body; @@ -2862,6 +3082,10 @@ public: bs->error("break is not inside a loop or switch"); return setError(); } + else if (sc->sbreak->isForwardingStatement()) + { + bs->error("must use labeled `break` within `static foreach`"); + } result = bs; } @@ -2944,6 +3168,10 @@ public: cs->error("continue is not inside a loop"); return setError(); } + else if (sc->scontinue->isForwardingStatement()) + { + cs->error("must use labeled `continue` within `static foreach`"); + } result = cs; } @@ -3663,3 +3891,20 @@ Statement *semanticScope(Statement *s, Scope *sc, Statement *sbreak, Statement * scd->pop(); return s; } + +/******************* + * See StatementSemanticVisitor.makeTupleForeach. This is a simple + * wrapper that returns the generated statements/declarations. + */ +Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion) +{ + StatementSemanticVisitor v = StatementSemanticVisitor(sc); + v.makeTupleForeachStatic(fs, needExpansion); + return v.result; +} + +Dsymbols *makeTupleForeachStaticDecl(Scope *sc, ForeachStatement *fs, Dsymbols *dbody, bool needExpansion) +{ + StatementSemanticVisitor v = StatementSemanticVisitor(sc); + return v.makeTupleForeachStaticDecl(fs, dbody, needExpansion); +} diff --git a/gcc/d/dmd/traits.c b/gcc/d/dmd/traits.c index 24303835268..04726c36473 100644 --- a/gcc/d/dmd/traits.c +++ b/gcc/d/dmd/traits.c @@ -1182,16 +1182,27 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) { if (!sm) return 1; + + // skip local symbols, such as static foreach loop variables + if (Declaration *decl = sm->isDeclaration()) + { + if (decl->storage_class & STClocal) + { + return 0; + } + } + //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); if (sm->ident) { - const char *idx = sm->ident->toChars(); - if (idx[0] == '_' && idx[1] == '_' && - sm->ident != Id::ctor && - sm->ident != Id::dtor && - sm->ident != Id::__xdtor && - sm->ident != Id::postblit && - sm->ident != Id::__xpostblit) + // 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; } @@ -1352,6 +1363,13 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) RootObject *o1 = (*e->args)[0]; RootObject *o2 = (*e->args)[1]; + + // issue 12001, allow isSame, , + Type *t1 = isType(o1); + Type *t2 = isType(o2); + if (t1 && t2 && t1->equals(t2)) + return True(e); + Dsymbol *s1 = getDsymbol(o1); Dsymbol *s2 = getDsymbol(o2); //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars()); @@ -1411,7 +1429,7 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) TupleExp *te= new TupleExp(e->loc, exps); return semantic(te, sc); } - else if(e->ident == Id::getVirtualIndex) + else if (e->ident == Id::getVirtualIndex) { if (dim != 1) return dimError(e, 1, dim); diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h index 4c9267044e2..df549da2df7 100644 --- a/gcc/d/dmd/visitor.h +++ b/gcc/d/dmd/visitor.h @@ -81,6 +81,7 @@ class TypeClass; class TypeTuple; class TypeSlice; class TypeNull; +class TypeTraits; class Dsymbol; @@ -107,6 +108,7 @@ class StaticIfDeclaration; class CompileDeclaration; class StaticForeachDeclaration; class UserAttributeDeclaration; +class ForwardingAttribDeclaration; class ScopeDsymbol; class TemplateDeclaration; @@ -373,6 +375,7 @@ public: virtual void visit(TypeTuple *t) { visit((Type *)t); } virtual void visit(TypeSlice *t) { visit((TypeNext *)t); } virtual void visit(TypeNull *t) { visit((Type *)t); } + virtual void visit(TypeTraits *t) { visit((Type *)t); } virtual void visit(Dsymbol *) { assert(0); } @@ -399,6 +402,7 @@ public: virtual void visit(StaticForeachDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); } + virtual void visit(ForwardingAttribDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(ScopeDsymbol *s) { visit((Dsymbol *)s); } virtual void visit(TemplateDeclaration *s) { visit((ScopeDsymbol *)s); } diff --git a/gcc/testsuite/gdc.test/compilable/b12001.d b/gcc/testsuite/gdc.test/compilable/b12001.d new file mode 100644 index 00000000000..f67c8957514 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/b12001.d @@ -0,0 +1,9 @@ +void main() +{ + static assert(__traits(isSame, int, int)); + static assert(__traits(isSame, int[][], int[][])); + static assert(__traits(isSame, bool*, bool*)); + + static assert(!__traits(isSame, bool*, bool[])); + static assert(!__traits(isSame, float, double)); +} diff --git a/gcc/testsuite/gdc.test/compilable/json.d b/gcc/testsuite/gdc.test/compilable/json.d index b5a560dcb2b..e2b08605893 100644 --- a/gcc/testsuite/gdc.test/compilable/json.d +++ b/gcc/testsuite/gdc.test/compilable/json.d @@ -111,3 +111,24 @@ enum Numbers } template IncludeConstraint(T) if (T == string) {} + +static foreach(enum i; 0..3) +{ + mixin("int a" ~ i.stringof ~ " = 1;"); +} + +alias Seq(T...) = T; + +static foreach(int i, alias a; Seq!(a0, a1, a2)) +{ + mixin("alias b" ~ i.stringof ~ " = a;"); +} + +mixin template test18211(int n) +{ + static foreach (i; 0 .. n>10 ? 10 : n) + { + mixin("enum x" ~ cast(char)('0' + i)); + } + static if (true) {} +} diff --git a/gcc/testsuite/gdc.test/compilable/staticforeach.d b/gcc/testsuite/gdc.test/compilable/staticforeach.d new file mode 100644 index 00000000000..48d06b418d3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/staticforeach.d @@ -0,0 +1,842 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: +// EXTRA_FILES: imports/imp12242a1.d imports/imp12242a2.d +/* +TEST_OUTPUT: +--- +9 +8 +7 +6 +5 +4 +3 +2 +1 +0 +S(1, 2, 3, [0, 1, 2]) +x0: 1 +x1: 2 +x2: 3 +a: [0, 1, 2] +(int[], char[], bool[], Object[]) +[0, 0] +x0: int +x1: double +x2: char +test(0)→ 0 +test(1)→ 1 +test(2)→ 2 +test(3)→ 3 +test(4)→ 4 +test(5)→ 5 +test(6)→ 6 +test(7)→ 7 +test(8)→ 8 +test(9)→ 9 +test(10)→ -1 +test(11)→ -1 +test(12)→ -1 +test(13)→ -1 +test(14)→ -1 +1 +[1, 2, 3] +2 +[1, 2, 3] +3 +[1, 2, 3] +0 1 +1 2 +2 3 +1 +3 +4 +object +Tuple +tuple +main +front +popFront +empty +back +popBack +Iota +iota +map +to +text +all +any +join +S +s +Seq +Overloads +Parameters +forward +foo +A +B +C +D +E +Types +Visitor +testVisitor +staticMap +arrayOf +StaticForeachLoopVariable +StaticForeachScopeExit +StaticForeachReverseHiding +UnrolledForeachReverse +StaticForeachReverse +StaticForeachByAliasDefault +NestedStaticForeach +TestAliasOutsideFunctionScope +OpApplyMultipleStaticForeach +OpApplyMultipleStaticForeachLowered +RangeStaticForeach +OpApplySingleStaticForeach +TypeStaticForeach +AliasForeach +EnumForeach +TestUninterpretable +SeqForeachConstant +SeqForeachBreakContinue +TestStaticForeach +testtest +fun +testEmpty +bug17660 +breakContinueBan +MixinTemplate +testToStatement +bug17688 +T +foo2 +T2 +1 2 '3' +2 3 '4' +0 1 +1 2 +2 3 +--- +*/ + +module staticforeach; + +struct Tuple(T...){ + T expand; + alias expand this; +} +auto tuple(T...)(T t){ return Tuple!T(t); } + +/+struct TupleStaticForeach{ // should work, but is not the fault of the static foreach implementation. + //pragma(msg, [tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)); + static foreach(a,b,c;[tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)){ + pragma(msg,a," ",b," ",c); + } +}+/ + +void main(){ + static foreach(a,b,c;[tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)){ + pragma(msg, a," ",b," ",c); + } + static struct S{ + // (aggregate scope, forward referencing possible) + static assert(stripA("123")==1); + static assert(stripA([1],2)==2); + static foreach(i;0..2){ + mixin(`import imports.imp12242a`~text(i+1)~`;`); + static assert(stripA("123")==1); + static assert(stripA([1],2)==2); + } + static assert(stripA("123")==1); + static assert(stripA([1],2)==2); + } + static foreach(i;0..2){ + // (function scope, no forward referencing) + mixin(`import imports.imp12242a`~text(i+1)~`;`); + static assert(stripA("123")==1); + static if(i) static assert(stripA([1],2)==2); + } + static assert(stripA("123")==1); + static assert(stripA([1],2)==2); +} + +auto front(T)(T[] a){ return a[0]; } +auto popFront(T)(ref T[] a){ a=a[1..$]; } +auto empty(T)(T[] a){ return !a.length; } +auto back(T)(T[] a){ return a[$-1]; } +auto popBack(T)(ref T[] a){ a=a[0..$-1]; } + +struct Iota(T){ + T s,e; + @property bool empty(){ return s>=e; } + @property T front(){ return s; } + @property T back(){ return cast(T)(e-1); } + void popFront(){ s++; } + void popBack(){ e--; } +} +auto iota(T)(T s, T e){ return Iota!T(s,e); } + +template map(alias a){ + struct Map(R){ + R r; + @property front(){ return a(r.front); } + @property back(){ return a(r.back); } + @property bool empty(){ return r.empty; } + void popFront(){ r.popFront(); } + void popBack(){ r.popBack(); } + } + auto map(R)(R r){ return Map!R(r); } +} + +template to(T:string){ + string to(S)(S x)if(is(S:int)||is(S:size_t)||is(S:char)){ + static if(is(S==char)) return cast(string)[x]; + if(x<0) return "-"~to(-1 * x); + if(x==0) return "0"; + return (x>=10?to(x/10):"")~cast(char)(x%10+'0'); + } +} +auto text(T)(T arg){ return to!string(arg); }; + +template all(alias a){ + bool all(R)(R r){ + foreach(x;r) if(!a(x)) return false; + return true; + } +} +template any(alias a){ + bool any(R)(R r){ + foreach(x;r) if(a(x)) return true; + return false; + } +} +auto join(R)(R r,string sep=""){ + string a; + int first=0; + foreach(x;r){ + if(first++) a~=sep; + a~=x; + } + return a; +} + +static foreach_reverse(x;iota(0,10).map!(to!string)){ + pragma(msg, x); +} + +// create struct members iteratively +struct S{ + static foreach(i;a){ + mixin("int x"~to!string(i)~";"); + } + immutable int[] a = [0,1,2]; +} +enum s=S(1,2,3); +pragma(msg, s); + +// loop over struct members +static foreach(member;__traits(allMembers,S)){ + pragma(msg, member,": ",mixin("s."~member)); +} + +// print prime numbers using overload sets as state variables. +/+ +static assert(is(typeof(bad57))); +static assert(!is(typeof(bad53))); + +static foreach(x;iota(2,100)){ + static foreach(y;iota(2,x)){ + static if(!(x%y)){ + mixin("void bad"~to!string(x)~"();"); + } + } + static if(!is(typeof(mixin("bad"~to!string(x))))){ + static assert(iota(2,x).all!(y=>!!(x%y))); + pragma(msg, x); + }else{ + static assert(iota(2,x).any!(y=>!(x%y))); + } +} ++/ + + +alias Seq(T...)=T; + +alias Overloads(alias a) = Seq!(__traits(getOverloads, __traits(parent, a), __traits(identifier, a))); + +template Parameters(alias f){ + static if(is(typeof(f) P == function)) alias Parameters=P; +} + +template forward(alias a){ + enum x=2; + static foreach(f;Overloads!a){ + auto ref forward(Parameters!f args){ + return f(args); + } + } + enum y=3; +} + +int foo(int x){ return x; } +string foo(string x){ return x; } + +static assert(forward!foo(2)==2 && forward!foo("hi") == "hi"); + + +// simple boilerplate-free visitor pattern +static foreach(char T;'A'..'F'){ + mixin("class "~T~q{{ + void accept(Visitor v){ + return v.visit(this); + } + }}); +} +alias Types = Seq!(mixin("Seq!("~iota('A','F').map!(to!string).join(", ")~")")); +abstract class Visitor{ + static foreach(T;Types){ + abstract void visit(T); + } +} + +string testVisitor(){ + string r; + void writeln(T...)(T args){ + static foreach(x;args) r~=x; + r~='\n'; + } + class Visitor: .Visitor{ + static foreach(T;Types){ + override void visit(T){ + writeln("visited: ",T.stringof); + } + } + } + void main(){ + auto v=new Visitor; + static foreach(T;Types){ + v.visit(new T); + } + } + main(); + return r; +} +static assert(testVisitor()=="visited: A +visited: B +visited: C +visited: D +visited: E +"); + +// iterative computation over AliasSeq: +template staticMap(alias F,T...){ + alias state0=Seq!(); + static foreach(i,A;T){ + mixin("alias state"~to!string(i+1)~" = Seq!(state"~to!string(i)~",F!A);"); + } + alias staticMap = Seq!(mixin("state"~to!string(T.length))); +} + +alias arrayOf(T)=T[]; +static assert(is(staticMap!(arrayOf,int,char,bool,Object)==Seq!(int[], char[], bool[], Object[]))); +pragma(msg, staticMap!(arrayOf,int,char,bool,Object)); + + +struct StaticForeachLoopVariable{ + int x; + static foreach(i;0..1){ + mixin("enum x"~text(i)~" = i;"); + } + int y; + static assert(__traits(allMembers, StaticForeachLoopVariable).length==3); + static assert(!is(typeof(StaticForeachLoopVariable.i))); + static assert(!is(typeof(__traits(getMember, StaticForeachLoopVariable, "i")))); +} + +struct StaticForeachScopeExit{ +static: + int[] test(){ + int[] r; + scope(exit) r ~= 1234; + { + static foreach(i;0..5){ + scope(exit) r ~= i; + } + r ~= 5; + } + return r; + } + static assert(test()==[5,4,3,2,1,0]); +} + +struct StaticForeachReverseHiding{ + static foreach(i;[0]){ + enum i = 1; // TODO: disallow? + static assert(i==0); + } +} + +struct UnrolledForeachReverse{ +static: + alias Seq(T...)=T; + int[] test(){ + int[] r; + foreach_reverse(i;Seq!(0,1,2,3)){ + r~=i; + } + return r; + } + static assert(test()==[3,2,1,0]); +} + +struct StaticForeachReverse{ +static: + alias Seq(T...)=T; + int[] test(){ + int[] r; + static foreach_reverse(i;0..4){ + r~=i; + } + return r; + } + static assert(test()==[3,2,1,0]); + + int[] test2(){ + int[] r; + static foreach_reverse(i;[0,1,2,3]){ + r~=i; + } + return r; + } + static assert(test2()==[3,2,1,0]); + + int[] test3(){ + static struct S{ + int opApplyReverse(scope int delegate(int) dg){ + foreach_reverse(i;0..4) if(auto r=dg(i)) return r; + return 0; + } + } + int[] r; + static foreach_reverse(i;S()){ + r~=i; + } + return r; + } + static assert(test3()==[3,2,1,0]); + + int[] test4(){ + int[] r; + static foreach_reverse(i;Seq!(0,1,2,3)){ + r~=i; + } + return r; + } + static assert(test()==[3,2,1,0]); +} + +struct StaticForeachByAliasDefault{ +static: + alias Seq(T...)=T; + + int[] test(){ + int a,b,c; + static foreach(i,x;Seq!(a,b,c)) x=i; + return [a,b,c]; + } + static assert(test()==[0,1,2]); + + int[] test2(){ + int x=0; + int foo(){ return ++x; } + static foreach(y;Seq!foo) + return [y,y,y]; + } + static assert(test2()==[1,2,3]); + + void test3(){ + int x=0; + int foo(){ return ++x; } + static assert(!is(typeof({ + static foreach(enum y;Seq!foo) + return [y,y,y]; + }))); + } +} + +struct NestedStaticForeach{ + static: + static foreach(i,name;["a"]){ + static foreach(j,name2;["d"]){ + mixin("enum int[] "~name~name2~"=[i, j];"); + } + } + pragma(msg, ad); +} + +struct TestAliasOutsideFunctionScope{ +static: + alias Seq(T...)=T; + int a; + static foreach(alias x;Seq!(a)){ + } +} + +struct OpApplyMultipleStaticForeach{ +static: + struct OpApply{ + int opApply(scope int delegate(int,int) dg){ + foreach(i;0..10) if(auto r=dg(i,i*i)) return r; + return 0; + } + } + static foreach(a,b;OpApply()){ + mixin(`enum x`~cast(char)('0'+a)~"=b;"); + } + static foreach(i;0..10){ + static assert(mixin(`x`~cast(char)('0'+i))==i*i); + } +} + + +struct OpApplyMultipleStaticForeachLowered{ +static: + struct OpApply{ + int opApply(scope int delegate(int,int) dg){ + foreach(i;0..10) if(auto r=dg(i,i*i)) return r; + return 0; + } + } + static foreach(x;{ + static struct S(T...){ this(T k){ this.x=k; } T x; } + static s(T...)(T a){ return S!T(a); } + typeof({ foreach(a,b;OpApply()){ return s(a,b); } assert(0);}())[] r; + foreach(a,b;OpApply()) r~=s(a,b); + return r; + }()){ + mixin(`enum x`~cast(char)('0'+x.x[0])~"=x.x[1];"); + } + static foreach(i;0..10){ + static assert(mixin(`x`~cast(char)('0'+i))==i*i); + } +} + +struct RangeStaticForeach{ + static: + struct Range{ + int x=0; + this(int x){ this.x=x; } + @property int front(){ return x; } + void popFront(){ x += 2; } + @property bool empty(){ return x>=10; } + } + static foreach(i;Range()){ + mixin(`enum x`~cast(char)('0'+i)~"=i;"); + } + static foreach(i;0..5){ + static assert(mixin(`x`~cast(char)('0'+2*i))==2*i); + } + static assert(!is(typeof({ + struct S{ + static foreach(i,k;Range()){} + } + }))); + static foreach(k;Range()){} // ok +} + +struct OpApplySingleStaticForeach{ + static: + struct OpApply{ + int opApply(scope int delegate(int) dg){ + foreach(i;0..10) if(auto r=dg(i)) return r; + return 0; + } + } + static foreach(b;OpApply()){ + mixin(`enum x`~cast(char)('0'+b)~"=b;"); + } + static foreach(i;0..10){ + static assert(mixin(`x`~cast(char)('0'+i))==i); + } +} + +struct TypeStaticForeach{ +static: + alias Seq(T...)=T; + static foreach(i,alias T;Seq!(int,double,char)){ + mixin(`T x`~cast(char)('0'+i)~";"); + } + pragma(msg, "x0: ",typeof(x0)); + pragma(msg, "x1: ",typeof(x1)); + pragma(msg, "x2: ",typeof(x2)); + static assert(is(typeof(x0)==int)); + static assert(is(typeof(x1)==double)); + static assert(is(typeof(x2)==char)); +} + +struct AliasForeach{ +static: + alias Seq(T...)=T; + int[] test(){ + int a,b,c; + static foreach(x;Seq!(a,b,c,2)){ + static if(is(typeof({x=2;}))) x=2; + } + int x,y,z; + static foreach(alias k;Seq!(x,y,z,2)){ + static if(is(typeof({k=2;}))) k=2; + } + int j,k,l; + static assert(!is(typeof({ + static foreach(ref x;Seq!(j,k,l,2)){ + static if(is(typeof({x=2;}))) x=2; + } + }))); + return [x,y,z]; + } + static assert(test()==[2,2,2]); +} + +struct EnumForeach{ +static: + alias Seq(T...)=T; + int a=1; + int fun(){ return 1; } + int gun(){ return 2; } + int hun(){ return 3;} + auto test(){ + static foreach(i,enum x;Seq!(fun,gun,hun)){ + static assert(i+1==x); + } + foreach(i,enum x;Seq!(fun,gun,hun)){ + static assert(i+1==x); + } + } +} + +struct TestUninterpretable{ +static: + alias Seq(T...)=T; + auto test(){ + int k; + static assert(!is(typeof({ + static foreach(x;[k]){} + }))); + static assert(!is(typeof({ + foreach(enum x;[1,2,3]){} + }))); + static assert(!is(typeof({ + foreach(alias x;[1,2,3]){} + }))); + foreach(enum x;Seq!(1,2,3)){} // ok + foreach(alias x;Seq!(1,2,3)){} // ok + static foreach(enum x;[1,2,3]){} // ok + static foreach(alias x;[1,2,3]){} // ok + static assert(!is(typeof({ + static foreach(enum alias x;[1,2,3]){} + }))); + int x; + static foreach(i;Seq!x){ } // ok + static foreach(i,j;Seq!(1,2,x)){ } // ok + static assert(!is(typeof({ + static foreach(ref x;[1,2,3]){} + }))); + } +} + +struct SeqForeachConstant{ +static: + alias Seq(T...)=T; + static assert(!is(typeof({ + foreach(x;Seq!1) x=2; + }))); + int test2(){ + int r=0; + foreach(x;Seq!(1,2,3)){ + enum k=x; + r+=k; + } + return r; + } + static assert(test2()==6); +} + +struct SeqForeachBreakContinue{ +static: + alias Seq(T...)=T; + int[] test(){ + int[] r; + foreach(i;Seq!(0,1,2,3,4,5)){ + if(i==2) continue; + if(i==4) break; + r~=i; + } + return r; + } + static assert(test()==[0,1,3]); +} + +struct TestStaticForeach{ +static: + int test(int x){ + int r=0; + label: switch(x){ + static foreach(i;0..10){ + case i: r=i; break label; // TODO: remove label when restriction is lifted + } + default: r=-1; break label; + } + return r; + } + static foreach(i;0..15){ + pragma(msg, "test(",i,")→ ",test(i)); + static assert(test(i)==(i<10?i:-1)); + } + + enum x=[1,2,3]; + + static foreach(i;x){ + mixin("enum x"~cast(char)('0'+i)~"="~cast(char)('0'+i)~";"); + } + + static foreach(i;x){ + pragma(msg, mixin("x"~cast(char)('0'+i))); + pragma(msg,x); + } + + int[] noBreakNoContinue(){ + int[] r; + static foreach(i;0..1){ + // if(i==3) continue; // TODO: error? + // if(i==7) break; // TODO: error? + r~=i; + } + return r; + } + + mixin("enum k=3;"); +} + +static foreach(i,j;[1,2,3]){ + pragma(msg, int(i)," ",j); +} + +void testtest(){ + static foreach(i,v;[1,2,3]){ + pragma(msg, int(i)," ",v); + static assert(i+1 == v); + } +} + + +static foreach(i;Seq!(1,2,3,4,int)){ + static if(!is(i) && i!=2){ + pragma(msg, i); + } +} + +int fun(int x){ + int r=0; + label: switch(x){ + static foreach(i;Seq!(0,1,2,3,4,5,6)){ + static if (i < 5) + case i: r=i; break label; // TODO: remove label when restriction is lifted + } + default: r=-1; break label; + } + return r; +} + +static foreach(i;0..10) static assert(fun(i)==(i<5?i:-1)); + +static foreach(i;0..0) { } +void testEmpty(){ + static foreach(i;0..0) { } +} + +auto bug17660(){ + int x; + static foreach (i; 0 .. 1) { return 3; } + return x; +} +static assert(bug17660()==3); + +int breakContinueBan(){ + static assert(!is(typeof({ + for(;;){ + static foreach(i;0..1){ + break; + } + } + }))); + static assert(!is(typeof({ + for(;;){ + static foreach(i;0..1){ + continue; + } + } + }))); + Louter1: for(;;){ + static foreach(i;0..1){ + break Louter1; + } + } + Louter2: foreach(i;0..10){ + static foreach(j;0..1){ + continue Louter2; + } + return 0; + } + static foreach(i;0..1){ + for(;;){ break; } // ok + } + return 1; +} +static assert(breakContinueBan()==1); + +mixin template MixinTemplate(){ + static foreach(i;0..2){ + mixin(`enum x`~cast(char)('0'+i)~"=i;"); + } + static foreach(i;[0,1]){ + mixin(`enum y`~cast(char)('0'+i)~"=i;"); + } +} +void testToStatement(){ + mixin MixinTemplate; + static assert(x0==0 && x1==1); + static assert(y0==0 && y1==1); +} + +void bug17688(){ + final switch(1) static foreach(x;0..1){ int y=3; case 1: return; } + static assert(!is(typeof(y))); +} + +struct T{ enum n = 1; } +T foo(T v)@nogc{ + static foreach(x;0..v.n){ } + return T.init; +} +T foo2(T v)@nogc{ + static foreach(_;0..typeof(return).n){ } + return T.init; +} + +//https://issues.dlang.org/show_bug.cgi?id=18698 + +static foreach(m; __traits(allMembers, staticforeach)) +{ + pragma(msg, m); +} + +//https://issues.dlang.org/show_bug.cgi?id=20072 +struct T2{ + static foreach(i;0..1) + struct S{} +} +static assert(is(__traits(parent,T2.S)==T2)); diff --git a/gcc/testsuite/gdc.test/compilable/test11169.d b/gcc/testsuite/gdc.test/compilable/test11169.d index 10a3df2c7a6..79863e11f47 100644 --- a/gcc/testsuite/gdc.test/compilable/test11169.d +++ b/gcc/testsuite/gdc.test/compilable/test11169.d @@ -43,3 +43,18 @@ void main() static assert(!__traits(compiles, { auto b = new B2(); })); static assert(!__traits(compiles, { auto b = new B3(); })); } + +class B : A +{ + // __traits(isAbstractClass) is not usable in static if condition. + static assert (!__traits(isAbstractClass, typeof(this))); + + override void foo() + { + } +} + +void main2() +{ + B b = new B(); +} diff --git a/gcc/testsuite/gdc.test/compilable/test17819.d b/gcc/testsuite/gdc.test/compilable/test17819.d new file mode 100644 index 00000000000..f1266a0aee1 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test17819.d @@ -0,0 +1,17 @@ +static if (__traits(allMembers, __traits(parent,{}))[0]=="object") { + enum test = 0; +} + +static foreach (m; __traits(allMembers, __traits(parent,{}))) { + mixin("enum new"~m~"=`"~m~"`;"); +} + +static assert([__traits(allMembers, __traits(parent,{}))] == ["object", "test", "newobject", "newWorld", "newBuildStuff", "World", "BuildStuff"]); + +struct World { + mixin BuildStuff; +} + +template BuildStuff() { + static foreach(elem; __traits(allMembers, typeof(this))) {} +} diff --git a/gcc/testsuite/gdc.test/compilable/test18871.d b/gcc/testsuite/gdc.test/compilable/test18871.d new file mode 100644 index 00000000000..44486f20cee --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test18871.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=18871 +// and https://issues.dlang.org/show_bug.cgi?id=18819 + +struct Problem +{ + ~this() {} +} +struct S +{ + Problem[1] payload; +} +enum theTemplateB = { + static foreach (e; S.init.tupleof) {} + return true; +}(); diff --git a/gcc/testsuite/gdc.test/compilable/test7815.d b/gcc/testsuite/gdc.test/compilable/test7815.d new file mode 100644 index 00000000000..405d9fc9d90 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test7815.d @@ -0,0 +1,65 @@ +// REQUIRED_ARGS: -o- +/* +TEST_OUTPUT: +--- +--- +*/ + +mixin template Helpers() +{ + static if (is(Flags!Move)) + { + Flags!Move flags; + } + else + { + pragma(msg, "X: ", __traits(derivedMembers, Flags!Move)); + } +} + +template Flags(T) +{ + mixin({ + int defs = 1; + foreach (name; __traits(derivedMembers, Move)) + { + defs++; + } + if (defs) + { + return "struct Flags { bool x; }"; + } + else + { + return ""; + } + }()); +} + +struct Move +{ + int a; + mixin Helpers!(); +} + +enum a7815 = Move.init.flags; + +/+ +This originally was an invalid case: +When the Move struct member is analyzed: +1. mixin Helpers!() is instantiated. +2. In Helpers!(), static if and its condition is(Flags!Move)) evaluated. +3. In Flags!Move, string mixin evaluates and CTFE lambda. +4. __traits(derivedMembers, Move) tries to see the member of Move. + 4a. mixin Helpers!() member is analyzed. + 4b. `static if (is(Flags!Move))` in Helpers!() is evaluated + 4c. The Flags!Move instantiation is already in progress, so it cannot be resolved. + 4d. `static if` fails because Flags!Move cannot be determined as a type. +5. __traits(derivedMembers, Move) returns a 1-length tuple("a"). +6. The lambda in Flags!Move returns a string "struct Flags {...}", then + Flags!Move is instantiated to a new struct Flags. +7. Finally Move struct does not have flags member, then the `enum a7815` + definition will fail in its initializer. + +Now, static if will behave like a string mixin: it is invisible during its own expansion. ++/ diff --git a/gcc/testsuite/gdc.test/compilable/test7886.d b/gcc/testsuite/gdc.test/compilable/test7886.d new file mode 100644 index 00000000000..fd3ade49685 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test7886.d @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=7886 + +struct A { + static assert (__traits(derivedMembers, A).length == 0); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/e7804_1.d b/gcc/testsuite/gdc.test/fail_compilation/e7804_1.d new file mode 100644 index 00000000000..38c25fbf7ae --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/e7804_1.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/e7804_1.d(10): Error: trait `farfelu` is either invalid or not supported as type +fail_compilation/e7804_1.d(11): Error: trait `farfelu` is either invalid or not supported in alias +--- +*/ +module e7804_1; + +__traits(farfelu, Aggr, "member") a; +alias foo = __traits(farfelu, Aggr, "member"); diff --git a/gcc/testsuite/gdc.test/fail_compilation/e7804_2.d b/gcc/testsuite/gdc.test/fail_compilation/e7804_2.d new file mode 100644 index 00000000000..ef9b784b24a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/e7804_2.d @@ -0,0 +1,19 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/e7804_2.d(17): Error: `__traits(getMember, Foo, "func")` does not give a valid type +--- +*/ + +module e7804_2; + +class Foo +{ + void func(){} +} + +void test() +{ + __traits(getMember, Foo, "func") var; + auto a = cast(__traits(getMember, Foo, "func")) 0; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11169.d b/gcc/testsuite/gdc.test/fail_compilation/fail11169.d deleted file mode 100644 index e6ab4a64886..00000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail11169.d +++ /dev/null @@ -1,28 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail11169.d(16): Error: error evaluating static if expression ---- -*/ - -class A -{ - abstract void foo(); -} - -class B : A -{ - // __traits(isAbstractClass) is not usable in static if condition. - static if (__traits(isAbstractClass, typeof(this))) - { - } - - override void foo() - { - } -} - -void main() -{ - B b = new B(); -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19182.d b/gcc/testsuite/gdc.test/fail_compilation/fail19182.d new file mode 100644 index 00000000000..388c4603f04 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19182.d @@ -0,0 +1,18 @@ +// REQUIRED_ARGS: -c +/* +TEST_OUTPUT: +--- +gigi +fail_compilation/fail19182.d(12): Error: `pragma(msg)` is missing a terminating `;` +--- +*/ + +void foo() +{ + pragma(msg, "gigi") // Here + static foreach (e; []) + { + pragma(msg, "lili"); + } + +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19336.d b/gcc/testsuite/gdc.test/fail_compilation/fail19336.d new file mode 100644 index 00000000000..fc15be5784f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19336.d @@ -0,0 +1,17 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail19336.d(14): Error: template instance `Template!()` template `Template` is not defined +fail_compilation/fail19336.d(14): Error: circular reference to `fail19336.Foo.a` +fail_compilation/fail19336.d(17): Error: circular reference to `fail19336.b` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=19336 + +struct Foo +{ + Template!() a(a.x); +} + +int b(b.x); diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19520.d b/gcc/testsuite/gdc.test/fail_compilation/fail19520.d new file mode 100644 index 00000000000..305e0555489 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19520.d @@ -0,0 +1,21 @@ +/* https://issues.dlang.org/show_bug.cgi?id=19520 +TEST_OUTPUT: +--- +fail_compilation/fail19520.d(17): Error: incompatible types for `(Empty) is (Empty)`: cannot use `is` with types +fail_compilation/fail19520.d(17): while evaluating: `static assert((Empty) is (Empty))` +fail_compilation/fail19520.d(18): Error: incompatible types for `(WithSym) is (WithSym)`: cannot use `is` with types +fail_compilation/fail19520.d(18): while evaluating: `static assert((WithSym) is (WithSym))` +fail_compilation/fail19520.d(19): Error: incompatible types for `(Empty) is (Empty)`: cannot use `is` with types +fail_compilation/fail19520.d(20): Error: incompatible types for `(WithSym) is (WithSym)`: cannot use `is` with types +--- +*/ +struct Empty { } +struct WithSym { int i; } + +void test() +{ + static assert(Empty is Empty); + static assert(WithSym is WithSym); + assert(Empty is Empty); + assert(WithSym is WithSym); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2195.d b/gcc/testsuite/gdc.test/fail_compilation/fail2195.d new file mode 100644 index 00000000000..b6d53042109 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail2195.d @@ -0,0 +1,18 @@ +// https://issues.dlang.org/show_bug.cgi?id=2195 +// REQUIRED_ARGS: -de +/* +TEST_OUTPUT: +--- +fail_compilation/fail2195.d(16): Deprecation: variable `variable` is shadowing variable `fail2195.main.variable`. Rename the `foreach` variable. +--- +*/ + +void main() +{ + int[int] arr; + int variable; + foreach (i, j; arr) + { + int variable; // shadowing is disallowed but not detected + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7815.d b/gcc/testsuite/gdc.test/fail_compilation/fail7815.d deleted file mode 100644 index ceb5923edb6..00000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail7815.d +++ /dev/null @@ -1,65 +0,0 @@ -// REQUIRED_ARGS: -o- -/* -TEST_OUTPUT: ---- -X: tuple("x") -fail_compilation/fail7815.d(47): Error: no property 'flags' for type 'Move' ---- -*/ - -mixin template Helpers() -{ - static if (is(Flags!Move)) - { - Flags!Move flags; - } - else - { - pragma(msg, "X: ", __traits(derivedMembers, Flags!Move)); - } -} - -template Flags(T) -{ - mixin({ - int defs = 1; - foreach (name; __traits(derivedMembers, Move)) - { - defs++; - } - if (defs) - { - return "struct Flags { bool x; }"; - } - else - { - return ""; - } - }()); -} - -struct Move -{ - int a; - mixin Helpers!(); -} - -enum a7815 = Move.init.flags; - -/+ -This is an invalid case. -When the Move struct member is analyzed: -1. mixin Helpers!() is instantiated. -2. In Helpers!(), static if and its condition is(Flags!Move)) evaluated. -3. In Flags!Move, string mixin evaluates and CTFE lambda. -4. __traits(derivedMembers, Move) tries to see the member of Move. - 4a. mixin Helpers!() member is analyzed. - 4b. `static if (is(Flags!Move))` in Helpers!() is evaluated - 4c. The Flags!Move instantiation is already in progress, so it cannot be resolved. - 4d. `static if` fails because Flags!Move cannot be determined as a type. -5. __traits(derivedMembers, Move) returns a 1-length tuple("a"). -6. The lambda in Flags!Move returns a string "struct Flags {...}", then - Flags!Move is instantiated to a new struct Flags. -7. Finally Move struct does not have flags member, then the `enum a7815` - definition will fail in its initializer. -+/ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7886.d b/gcc/testsuite/gdc.test/fail_compilation/fail7886.d deleted file mode 100644 index b939aad1495..00000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail7886.d +++ /dev/null @@ -1,5 +0,0 @@ -// 7886 - -struct A { - static if (__traits(derivedMembers, A).length) {} -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/staticforeach1.d b/gcc/testsuite/gdc.test/fail_compilation/staticforeach1.d new file mode 100644 index 00000000000..b58f520c0bf --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/staticforeach1.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/staticforeach1.d(10): Error: must use labeled `break` within `static foreach` +--- +*/ +void main(){ + for(;;){ + static foreach(i;0..1){ + break; + } + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/staticforeach2.d b/gcc/testsuite/gdc.test/fail_compilation/staticforeach2.d new file mode 100644 index 00000000000..25e283efb5c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/staticforeach2.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/staticforeach2.d(10): Error: must use labeled `continue` within `static foreach` +--- +*/ +void main(){ + for(;;){ + static foreach(i;0..1){ + continue; + } + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/staticforeach3.d b/gcc/testsuite/gdc.test/fail_compilation/staticforeach3.d new file mode 100644 index 00000000000..a93d20be86f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/staticforeach3.d @@ -0,0 +1,7 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/staticforeach3.d(7): Error: variable `staticforeach3.__anonymous.i` conflicts with variable `staticforeach3.__anonymous.i` at fail_compilation/staticforeach3.d(7) +--- +*/ +static foreach(i,i;[0]){} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17307.d b/gcc/testsuite/gdc.test/fail_compilation/test17307.d new file mode 100644 index 00000000000..470cfed7eba --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test17307.d @@ -0,0 +1,12 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test17307.d(9): Error: anonymous struct can only be a part of an aggregate, not module `test17307` +--- + * https://issues.dlang.org/show_bug.cgi?id=17307 + */ + +struct { enum bitsPerWord = size_t; } + +void main() +{ } diff --git a/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d b/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d new file mode 100644 index 00000000000..8f6f145e6bf --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d @@ -0,0 +1,10 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/traits_alone.d(11): Error: found `End of File` when expecting `(` +fail_compilation/traits_alone.d(11): Error: `__traits(identifier, args...)` expected +fail_compilation/traits_alone.d(11): Error: no identifier for declarator `_error_` +--- +*/ +//used to segfault +__traits diff --git a/gcc/testsuite/gdc.test/runnable/arrayop.d b/gcc/testsuite/gdc.test/runnable/arrayop.d index 99bf800f610..e3749bee72a 100644 --- a/gcc/testsuite/gdc.test/runnable/arrayop.d +++ b/gcc/testsuite/gdc.test/runnable/arrayop.d @@ -919,8 +919,7 @@ int main() } else { - pragma(msg, "arrayop.d:test1 Test skipped because arrayop evaluation" - " order is ill-defined. See GDC issue #8"); + //pragma(msg, "Test skipped because arrayop evaluation order is ill-defined."); } test3(); test4(); diff --git a/gcc/testsuite/gdc.test/runnable/constfold.d b/gcc/testsuite/gdc.test/runnable/constfold.d index 57da8b75ba2..0708056bc66 100644 --- a/gcc/testsuite/gdc.test/runnable/constfold.d +++ b/gcc/testsuite/gdc.test/runnable/constfold.d @@ -252,15 +252,15 @@ void test2() // This test only tests undefined, architecture-dependant behavior. // E.g. the result of converting a float whose value doesn't fit into the integer // leads to an undefined result. - version(GNU) - return; - - float f = float.infinity; - int i = cast(int) f; - writeln(i); - writeln(cast(int)float.max); - assert(i == cast(int)float.max); - assert(i == 0x80000000); + version (DigitalMars) + { + float f = float.infinity; + int i = cast(int) f; + writeln(i); + writeln(cast(int)float.max); + assert(i == cast(int)float.max); + assert(i == 0x80000000); + } } /************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/e7804.d b/gcc/testsuite/gdc.test/runnable/e7804.d new file mode 100644 index 00000000000..d32531055f5 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/e7804.d @@ -0,0 +1,179 @@ +/* REQUIRED_ARGS: -unittest +*/ +module e7804; + +struct Bar {static struct B{}} +alias BarB = __traits(getMember, Bar, "B"); +static assert(is(BarB == Bar.B)); +static assert(is(const(__traits(getMember, Bar, "B")) == const(Bar.B))); + +alias BarBParent = __traits(parent, BarB); +static assert(is(BarBParent == Bar)); + +struct Foo {alias MyInt = int;} +alias FooInt = __traits(getMember, Foo, "MyInt"); +static immutable FooInt fi = 42; +static assert(fi == 42); +void declVsStatementSupport() +{ + __traits(getMember, Foo, "MyInt") i1 = 1; + const(__traits(getMember, Foo, "MyInt")) i2 = 1; + assert(i1 == i2); + __traits(getMember, Foo, "MyInt") i3 = __traits(getMember, Foo, "MyInt").max; + assert(i3 == int.max); +} + + +enum __traits(getMember, Foo, "MyInt") a0 = 12; +static assert(is(typeof(a0) == int)); +static assert(a0 == 12); + + +const __traits(getMember, Foo, "MyInt") a1 = 46; + + +__traits(getMember, Foo, "MyInt") a2 = 78; + + +const(__traits(getMember, Foo, "MyInt")) a3 = 63; + + +struct WithSym {static int foo; static int bar(){return 42;}} +alias m1 = __traits(getMember, WithSym, "foo"); +alias m2 = WithSym.foo; +static assert(__traits(isSame, m1, m2)); +alias f1 = __traits(getMember, WithSym, "bar"); +alias f2 = WithSym.bar; +static assert(__traits(isSame, f1, f2)); + + +auto ovld(const(char)[] s){return s;} +auto ovld(int i){return i;} +alias ovlds = __traits(getOverloads, e7804, "ovld"); + + +struct TmpPrm(T) +if (is(T == int)){T t;} +TmpPrm!(__traits(getMember, Foo, "MyInt")) tpt = TmpPrm!(__traits(getMember, Foo, "MyInt"))(42); + + +@Foo @(1) class Class +{ + final void virtual(){} + int virtual(int p){return p;} + void test(this T)() + { + alias vf = __traits(getVirtualFunctions, Class, "virtual"); + assert(vf.length == 2); + alias vm = __traits(getVirtualMethods, Class, "virtual"); + assert(vm.length == 1); + assert(vm[0](42) == 42); + alias attribs = __traits(getAttributes, Class); + assert(attribs.length == 2); + assert(is(typeof(attribs[0]()) == Foo)); + assert(attribs[1] == 1); + + alias objectAll = __traits(allMembers, Object); + alias classDerived = __traits(derivedMembers, Class); + alias classAll = __traits(allMembers, Class); + enum Seq(T...) = T; + static assert (classAll == Seq!(classDerived, objectAll)); + } +} + + +struct UnitTests +{ + static int count; + unittest { count++; } + unittest {++++count;} + static void test() + { + alias tests = __traits(getUnitTests, UnitTests); + static assert(tests.length == 2); + foreach(t; tests) t(); + assert(count == 6); // not 3 because executed automatically (DRT) then manually + } +} + + +class One +{ + void foo(){} + void foo(int){} +} + +class Two : One +{ + void test() + { + alias Seq(T...) = T; + alias p1 = Seq!(__traits(getMember, super, "foo"))[0]; + alias p2 = __traits(getMember, super, "foo"); + static assert(__traits(isSame, p1, p2)); + } +} + + +class SingleSymTuple +{ + int foo(){return 42;} + void test() + { + alias f = __traits(getMember, this, "foo"); + assert(f() == 42); + } +} + + +struct WithAliasThis +{ + auto getter(){return 42;} + alias getter this; + void test() + { + alias getterCall = __traits(getAliasThis, typeof(this)); + assert(mixin(getterCall[0]) == 42); + } +} + +void main() +{ + declVsStatementSupport(); + assert(a1 == 46); + assert(a2 == 78); + assert(a3 == 63); + assert(f1() == f2()); + Foo.MyInt fmi = cast(__traits(getMember, Foo, "MyInt")) 0; + auto c = __traits(getMember, Foo, "MyInt").max; + assert(c == int.max); + assert(ovlds[0]("farfelu") == "farfelu"); + assert(ovlds[1](42) == 42); + (new Class).test(); + UnitTests.test(); + (new WithAliasThis).test(); + (new Two).test(); + (new SingleSymTuple).test(); +} + +/* https://issues.dlang.org/show_bug.cgi?id=19708 */ +struct Foo19708 {} +struct Bar19708 {} +template Baz19708(T) { struct Baz19708{T t;} } +int symbol19708; + +@Foo19708 @Bar19708 @Baz19708 @symbol19708 int bar19708; + +alias TR19708 = __traits(getAttributes, bar19708); +alias TRT = __traits(getAttributes, bar19708)[2]; + +TR19708[0] a119708; +TR19708[1] a219708; +alias A3 = TRT!int; + +alias C19708 = TR19708[0]; +alias D19708 = TR19708[1]; +C19708 c1; +D19708 d1; + +static assert(__traits(isSame, TR19708[3], symbol19708)); diff --git a/gcc/testsuite/gdc.test/runnable/imports/template13478a.d b/gcc/testsuite/gdc.test/runnable/imports/template13478a.d index 9fcecf3e382..0c390bc9cfe 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/template13478a.d +++ b/gcc/testsuite/gdc.test/runnable/imports/template13478a.d @@ -1,9 +1,8 @@ module imports.template13478a; -import gcc.attribute; - -@attribute("noinline") bool foo(T)() { +bool foo(T)() { // Make sure this is not inlined so template13478.o actually // needs to reference it. + pragma(inline, false); return false; } diff --git a/gcc/testsuite/gdc.test/runnable/staticforeach.d b/gcc/testsuite/gdc.test/runnable/staticforeach.d new file mode 100644 index 00000000000..bf6dc983935 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/staticforeach.d @@ -0,0 +1,45 @@ +// REQUIRED_ARGS: + +/**********************************/ +// https://issues.dlang.org/show_bug.cgi?id=19479 + +mixin template genInts19479a() +{ + static foreach (t; 0..1) + int i = 5; +} + +mixin template genInts19479b() +{ + static foreach (t; 0..2) + mixin("int i" ~ cast(char)('0' + t) ~ " = 5;"); +} + +void test19479() +{ + { + static foreach (t; 0..1) + int i = 5; + assert(i == 5); + } + { + mixin genInts19479a!(); + assert(i == 5); + } + { + static foreach (t; 0..2) + mixin("int i" ~ cast(char)('0' + t) ~ " = 5;"); + assert(i0 == 5); + assert(i1 == 5); + } + { + mixin genInts19479b!(); + assert(i0 == 5); + assert(i1 == 5); + } +} + +void main() +{ + test19479(); +} diff --git a/gcc/testsuite/gdc.test/runnable/test42.d b/gcc/testsuite/gdc.test/runnable/test42.d index 76f8e212358..6e0c42b6ff5 100644 --- a/gcc/testsuite/gdc.test/runnable/test42.d +++ b/gcc/testsuite/gdc.test/runnable/test42.d @@ -1682,54 +1682,13 @@ void test101() /***************************************************/ -version(GNU) -{ -int x103; - -void external(int a, ...) -{ - va_list ap; - va_start(ap, a); - auto ext = va_arg!int(ap); - printf("external: %d\n", ext); - x103 = ext; - va_end(ap); -} - -class C103 -{ - void method () - { - void internal (int a, ...) - { - va_list ap; - va_start(ap, a); - auto internal = va_arg!int(ap); - printf("internal: %d\n", internal); - x103 = internal; - va_end(ap); - } - - internal (0, 43); - assert(x103 == 43); - } -} - -void test103() -{ - external(0, 42); - assert(x103 == 42); - (new C103).method (); -} -} -else version(X86) -{ int x103; void external(...) { - printf("external: %d\n", *cast (int *) _argptr); - x103 = *cast (int *) _argptr; + int arg = va_arg!int(_argptr); + printf("external: %d\n", arg); + x103 = arg; } class C103 @@ -1738,8 +1697,9 @@ class C103 { void internal (...) { - printf("internal: %d\n", *cast (int *)_argptr); - x103 = *cast (int *) _argptr; + int arg = va_arg!int(_argptr); + printf("internal: %d\n", arg); + x103 = arg; } internal (43); @@ -1753,14 +1713,6 @@ void test103() assert(x103 == 42); (new C103).method (); } -} -else version(X86_64) -{ - pragma(msg, "Not ported to x86-64 compatible varargs, yet."); - void test103() {} -} -else - static assert(false, "Unknown platform"); /***************************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/traits.d b/gcc/testsuite/gdc.test/runnable/traits.d index ef23e9f32be..6c3bf7859e3 100644 --- a/gcc/testsuite/gdc.test/runnable/traits.d +++ b/gcc/testsuite/gdc.test/runnable/traits.d @@ -1247,14 +1247,35 @@ struct S10096X invariant() {} invariant() {} unittest {} + unittest {} this(int) {} this(this) {} ~this() {} + + string getStr() in(str) out(r; r == str) { return str; } } static assert( [__traits(allMembers, S10096X)] == - ["str", "__ctor", "__postblit", "__dtor", "__xdtor", "__xpostblit", "opAssign"]); + ["str", "__ctor", "__postblit", "__dtor", "getStr", "__xdtor", "__xpostblit", "opAssign"]); + +class C10096X +{ + string str; + + invariant() {} + invariant() {} + unittest {} + unittest {} + + this(int) {} + ~this() {} + + string getStr() in(str) out(r; r == str) { return str; +} +static assert( + [__traits(allMembers, C10096X)] == + ["str", "__ctor", "__dtor", "getStr", "__xdtor", "toString", "toHash", "opCmp", "opEquals", "Monitor", "factory"]); // -------- @@ -1525,6 +1546,21 @@ void async(ARGS...)(ARGS) alias test17495 = async!(int, int); +/********************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=10100 + +enum E10100 +{ + value, + _value, + __value, + ___value, + ____value, +} +static assert( + [__traits(allMembers, E10100)] == + ["value", "_value", "__value", "___value", "____value"]); + /********************************************************/ int main()