diff options
Diffstat (limited to 'gcc/d')
-rw-r--r-- | gcc/d/dmd/MERGE | 2 | ||||
-rw-r--r-- | gcc/d/dmd/attrib.c | 189 | ||||
-rw-r--r-- | gcc/d/dmd/attrib.h | 9 | ||||
-rw-r--r-- | gcc/d/dmd/cond.c | 333 | ||||
-rw-r--r-- | gcc/d/dmd/cond.h | 5 | ||||
-rw-r--r-- | gcc/d/dmd/cppmangle.c | 2 | ||||
-rw-r--r-- | gcc/d/dmd/declaration.c | 9 | ||||
-rw-r--r-- | gcc/d/dmd/dinterpret.c | 4 | ||||
-rw-r--r-- | gcc/d/dmd/dmangle.c | 1 | ||||
-rw-r--r-- | gcc/d/dmd/dsymbol.c | 100 | ||||
-rw-r--r-- | gcc/d/dmd/dsymbol.h | 2 | ||||
-rw-r--r-- | gcc/d/dmd/expression.c | 10 | ||||
-rw-r--r-- | gcc/d/dmd/expression.h | 2 | ||||
-rw-r--r-- | gcc/d/dmd/expressionsem.c | 27 | ||||
-rw-r--r-- | gcc/d/dmd/func.c | 4 | ||||
-rw-r--r-- | gcc/d/dmd/hdrgen.c | 68 | ||||
-rw-r--r-- | gcc/d/dmd/init.c | 2 | ||||
-rw-r--r-- | gcc/d/dmd/intrange.c | 2 | ||||
-rw-r--r-- | gcc/d/dmd/json.c | 4 | ||||
-rw-r--r-- | gcc/d/dmd/mtype.c | 152 | ||||
-rw-r--r-- | gcc/d/dmd/mtype.h | 18 | ||||
-rw-r--r-- | gcc/d/dmd/parse.c | 304 | ||||
-rw-r--r-- | gcc/d/dmd/parse.h | 3 | ||||
-rw-r--r-- | gcc/d/dmd/scope.h | 3 | ||||
-rw-r--r-- | gcc/d/dmd/statement.c | 120 | ||||
-rw-r--r-- | gcc/d/dmd/statement.h | 9 | ||||
-rw-r--r-- | gcc/d/dmd/statementsem.c | 581 | ||||
-rw-r--r-- | gcc/d/dmd/traits.c | 34 | ||||
-rw-r--r-- | gcc/d/dmd/visitor.h | 4 |
29 files changed, 1681 insertions, 322 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 578f3fc..b017c03 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 6cd715c..86485d2 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 d1f265a..ccfcdda 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 9d7df5f..c75399d 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 8e33b16..576de8c 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 9b24fd2..6179bfd 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 0018d95..806e29d 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 a1658bb..61f5cdb 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 44f4f82..f41f628 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 9aec87a..05ab04c 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 a840261..788b67e 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 5f1bfa8..ccfb4b6 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 b460e8c..6044860 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 c23e332..781bd3e 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 11e4b2f..ab74dc5 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 395aa32..2436f6e 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 b40ebe3..7bd44ab 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 e0e2472..c56d7ba 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 acdafa5..fa49e92 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 b76b5ba..aa18806 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 aab0d03..22fabf5 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 9da58af..b66bddb 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(); @@ -4700,6 +4717,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 * Output: @@ -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 97630dc..c5ef0b2 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 37a15fc..d34a0e7 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 450b3f4..6c3443c 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 fae0862..8f69383 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 cc2b63e..26e5950 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 2430383..04726c3 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, <BasicType>, <BasicType> + 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 4c92670..df549da2 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); } |