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/fail_compilation/fail7815.d b/gcc/testsuite/gdc.test/compilable/test7815.d similarity index 92% rename from gcc/testsuite/gdc.test/fail_compilation/fail7815.d rename to gcc/testsuite/gdc.test/compilable/test7815.d index ceb5923edb6..405d9fc9d90 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail7815.d +++ b/gcc/testsuite/gdc.test/compilable/test7815.d @@ -2,8 +2,6 @@ /* TEST_OUTPUT: --- -X: tuple("x") -fail_compilation/fail7815.d(47): Error: no property 'flags' for type 'Move' --- */ @@ -47,7 +45,7 @@ struct Move enum a7815 = Move.init.flags; /+ -This is an invalid case. +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. @@ -62,4 +60,6 @@ When the Move struct member is analyzed: 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/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()