-b37a537d36c2ac69afa505a3110e2328c9fc0114
+e9420cfbf5cd0cf9e6e398603e009ccc8e14d324
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
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 ****************************/
Dsymbols *AttribDeclaration::include(Scope *, ScopeDsymbol *)
{
+ if (errors)
+ return NULL;
+
return decl;
}
{
::error(loc, "%s can only be a part of an aggregate, not %s %s",
kind(), p->kind(), p->toChars());
+ errors = true;
return;
}
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;
}
//printf("StaticIfDeclaration::StaticIfDeclaration()\n");
scopesym = NULL;
addisdone = false;
+ onStack = false;
}
Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s)
{
//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)
{
addisdone = true;
}
+ onStack = false;
return d;
}
else
{
- return ConditionalDeclaration::include(sc, scopesym);
+ d = ConditionalDeclaration::include(sc, scopesym);
+ onStack = false;
+ return d;
}
}
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");
public:
ScopeDsymbol *scopesym;
bool addisdone;
+ bool onStack;
StaticIfDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl);
Dsymbol *syntaxCopy(Dsymbol *s);
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);
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
#include "mars.h"
#include "id.h"
#include "init.h"
+#include "aggregate.h"
#include "declaration.h"
#include "identifier.h"
#include "expression.h"
#include "template.h"
#include "mtype.h"
#include "scope.h"
+#include "statement.h"
#include "arraytypes.h"
#include "tokens.h"
/* ============================================================ */
+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())
{
: Condition(loc)
{
this->exp = exp;
- this->nest = 0;
}
Condition *StaticIfCondition::syntaxCopy()
{
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");
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
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:
{
public:
Expression *exp;
- int nest; // limit circular dependencies
StaticIfCondition(Loc loc, Expression *exp);
Condition *syntaxCopy();
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();
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();
if (fd || (td && td->literal))
{
if (fd && fd->semanticRun >= PASSsemanticdone)
+ {
+ sc->flags &= ~SCOPEalias;
return;
+ }
Expression *e = new FuncExp(loc, aliassym);
e = ::semantic(e, sc);
aliassym = NULL;
type = Type::terror;
}
+ sc->flags &= ~SCOPEalias;
return;
}
if (aliassym->isTemplateInstance())
aliassym->semantic(sc);
+ sc->flags &= ~SCOPEalias;
return;
}
inuse = 1;
if (!overloadInsert(sx))
ScopeDsymbol::multiplyDefined(Loc(), sx, this);
}
+ sc->flags &= ~SCOPEalias;
}
bool AliasDeclaration::overloadInsert(Dsymbol *s)
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))
mangleChar[Tslice] = "@";
mangleChar[Treturn] = "@";
mangleChar[Tvector] = "@";
+ mangleChar[Ttraits] = "@";
mangleChar[Tnull] = "n"; // same as TypeNone
*/
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
{
//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;
/// 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
}
+/********************************* 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()
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);
return NULL;
}
+TupleExp *Expression::toTupleExp()
+{
+ return NULL;
+}
+
/***************************************
* Return !=0 if expression is an lvalue.
*/
return new TupleExp(loc, e0 ? e0->syntaxCopy() : NULL, arraySyntaxCopy(exps));
}
+TupleExp *TupleExp::toTupleExp()
+{
+ return this;
+}
+
/******************************** FuncExp *********************************/
FuncExp::FuncExp(Loc loc, Dsymbol *s)
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);
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);
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();
+ }
+ }
}
}
}
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())
* 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())
{
if (fld->tok != TOKfunction)
fld->tok = TOKdelegate;
}
- assert(!isNested());
}
}
buf->writenl();
}
- void visit(ForeachStatement *s)
+ void foreachWithoutBody(ForeachStatement *s)
{
buf->writestring(Token::toChars(s->op));
buf->writestring(" (");
s->aggr->accept(this);
buf->writeByte(')');
buf->writenl();
+ }
+
+ void visit(ForeachStatement *s)
+ {
+ foreachWithoutBody(s);
buf->writeByte('{');
buf->writenl();
buf->level++;
buf->writenl();
}
- void visit(ForeachRangeStatement *s)
+ void foreachRangeWithoutBody(ForeachRangeStatement *s)
{
buf->writestring(Token::toChars(s->op));
buf->writestring(" (");
buf->writenl();
buf->writeByte('{');
buf->writenl();
+ }
+
+ void visit(ForeachRangeStatement *s)
+ {
+ foreachRangeWithoutBody(s);
buf->level++;
if (s->_body)
s->_body->accept(this);
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 (");
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);
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(");
void visit(AliasDeclaration *d)
{
+ if (d->storage_class & STClocal)
+ return;
buf->writestring("alias ");
if (d->aliassym)
{
void visit(VarDeclaration *d)
{
+ if (d->storage_class & STClocal)
+ return;
visitVarDecl(d, false);
buf->writeByte(';');
buf->writenl();
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++)
{ STCsystem, TOKat, "@system" },
{ STCdisable, TOKat, "@disable" },
{ STCfuture, TOKat, "@__future" },
+ { STClocal, TOKat, "__local" },
{ 0, TOKreserved, NULL }
};
return arrayHasNonConstPointers(ae->keys);
return false;
}
- if(e->op == TOKaddress)
+ if (e->op == TOKaddress)
{
AddrExp *ae = (AddrExp *)e;
if (ae->e1->op == TOKstructliteral)
{
r.imax.value--;
}
- else if(r.imin.value == 0)
+ else if (r.imin.value == 0)
{
r.imin.value++;
}
void jsonProperties(Declaration *d)
{
+ if (d->storage_class & STClocal)
+ return;
jsonProperties((Dsymbol *)d);
propertyStorageClass("storageClass", d->storage_class);
void visit(VarDeclaration *d)
{
+ if (d->storage_class & STClocal)
+ return;
objectStart();
jsonProperties(d);
sizeTy[Terror] = sizeof(TypeError);
sizeTy[Tnull] = sizeof(TypeNull);
sizeTy[Tvector] = sizeof(TypeVector);
+ sizeTy[Ttraits] = sizeof(TypeTraits);
initTypeMangle();
* 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)
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 *****************************/
Tvector,
Tint128,
Tuns128,
+ Ttraits,
TMAX
};
typedef unsigned char TY; // ENUMTY
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:
case TOKunion:
case TOKclass:
case TOKinterface:
+ case TOKtraits:
Ldeclaration:
a = parseDeclarations(false, pAttrs, pAttrs->comment);
if (a && a->dim)
a = parseImport();
// keep pLastDecl
}
+ else if (next == TOKforeach || next == TOKforeach_reverse)
+ {
+ s = parseForeachStaticDecl(token.loc, pLastDecl);
+ }
else
{
stc = STCstatic;
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();
}
+/*****************************************
+ * 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
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.
*/
case TOKtypeid:
case TOKis:
case TOKlbracket:
- case TOKtraits:
case TOKfile:
case TOKfilefullpath:
case TOKline:
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();
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;
}
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:
case TOKfunction:
case TOKdelegate:
case TOKtypeof:
+ case TOKtraits:
case TOKvector:
case TOKfile:
case TOKfilefullpath:
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();
#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
{
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)
{
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 *) {}
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));
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)
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)
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); }
public:
StaticForeach *sfe;
+ StaticForeachStatement(Loc loc, StaticForeach *sfe);
Statement *syntaxCopy();
Statements *flatten(Scope *sc);
#include "errors.h"
#include "statement.h"
+#include "attrib.h"
#include "expression.h"
#include "cond.h"
#include "init.h"
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);
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
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);
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;
}
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:
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;
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;
}
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;
}
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);
+}
{
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;
}
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());
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);
class TypeTuple;
class TypeSlice;
class TypeNull;
+class TypeTraits;
class Dsymbol;
class CompileDeclaration;
class StaticForeachDeclaration;
class UserAttributeDeclaration;
+class ForwardingAttribDeclaration;
class ScopeDsymbol;
class TemplateDeclaration;
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); }
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); }
--- /dev/null
+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));
+}
}
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) {}
+}
--- /dev/null
+// 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));
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();
+}
--- /dev/null
+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))) {}
+}
--- /dev/null
+// 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;
+}();
/*
TEST_OUTPUT:
---
-X: tuple("x")
-fail_compilation/fail7815.d(47): Error: no property 'flags' for type '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.
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.
+/
--- /dev/null
+// https://issues.dlang.org/show_bug.cgi?id=7886
+
+struct A {
+ static assert (__traits(derivedMembers, A).length == 0);
+}
--- /dev/null
+/*
+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");
--- /dev/null
+/*
+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;
+}
+++ /dev/null
-/*
-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();
-}
--- /dev/null
+// 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");
+ }
+
+}
--- /dev/null
+/*
+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);
--- /dev/null
+/* 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);
+}
--- /dev/null
+// 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
+ }
+}
+++ /dev/null
-// 7886
-
-struct A {
- static if (__traits(derivedMembers, A).length) {}
-}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/staticforeach1.d(10): Error: must use labeled `break` within `static foreach`
+---
+*/
+void main(){
+ for(;;){
+ static foreach(i;0..1){
+ break;
+ }
+ }
+}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/staticforeach2.d(10): Error: must use labeled `continue` within `static foreach`
+---
+*/
+void main(){
+ for(;;){
+ static foreach(i;0..1){
+ continue;
+ }
+ }
+}
--- /dev/null
+/*
+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]){}
--- /dev/null
+/*
+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()
+{ }
--- /dev/null
+/*
+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
}
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();
// 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);
+ }
}
/************************************/
--- /dev/null
+/* 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));
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;
}
--- /dev/null
+// 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();
+}
/***************************************************/
-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
{
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);
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");
/***************************************************/
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"]);
// --------
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()