d/dmd: Merge upstream dmd e9420cfbf
authorIain Buclaw <ibuclaw@gdcproject.org>
Fri, 13 Mar 2020 20:03:02 +0000 (21:03 +0100)
committerIain Buclaw <ibuclaw@gdcproject.org>
Fri, 13 Mar 2020 20:03:02 +0000 (21:03 +0100)
1. Implement DIP 1010 - (Static foreach)

Support for 'static foreach' has been added.  'static foreach' is a conditional
compilation construct that is to 'foreach' what 'static if' is to 'if'.  It is
a convenient way to generate declarations and statements by iteration.

    import std.conv: to;

    static foreach(i; 0 .. 10)
    {

        // a 'static foreach' body does not introduce a nested scope
        // (similar to 'static if').

        // The following mixin declaration is at module scope:
        // declares 10 variables x0, x1, ..., x9
        mixin('enum x' ~ to!string(i) ~ ' = i;');
    }

    import std.range: iota;
    // all aggregate types that can be iterated with a standard 'foreach'
    // loop are also supported by static foreach:
    static foreach(i; iota(10))
    {
        // we access the declarations generated in the first 'static foreach'
        pragma(msg, "x", i, ": ", mixin(`x` ~ to!string(i)));
        static assert(mixin(`x` ~ to!string(i)) == i);
    }

    void main()
    {
        import std.conv: text;
        import std.typecons: tuple;
        import std.algorithm: map;
        import std.stdio: writeln;

        // 'static foreach' has both declaration and statement forms
        // (similar to 'static if').

        static foreach(x; iota(3).map!(i => tuple(text("x", i), i)))
        {
            // generates three local variables x0, x1 and x2.
            mixin(text(`int `,x[0],` = x[1];`));

            scope(exit) // this is within the scope of 'main'
            {
                writeln(mixin(x[0]));
            }
        }

        writeln(x0," ",x1," ",x2); // first runtime output
    }

2. Aliases can be created directly from a '__trait'.

Aliases can be created directly from the traits that return symbol(s) or
tuples.  This includes 'getMember', 'allMembers', 'derivedMembers', 'parent',
'getOverloads', 'getVirtualFunctions', 'getVirtualMethods', 'getUnitTests',
'getAttributes' and finally 'getAliasThis'.  Previously an 'AliasSeq' was
necessary in order to alias their return.  Now the grammar allows to write
shorter declarations:

    struct Foo
    {
        static int a;
    }

    alias oldWay = AliasSeq!(__traits(getMember, Foo, "a"))[0];
    alias newWay = __traits(getMember, Foo, "a");

To permit this it was more interesting to include '__trait' in the basic types
rather than just changing the alias syntax. So additionally, wherever a type
appears a '__trait' can be used, for example in a variable declaration:

    struct Foo { static struct Bar {} }
    const(__traits(getMember, Foo, "Bar")) fooBar;
    static assert(is(typeof(fooBar) == const(Foo.Bar)));

3. fix Issue 10100 - Identifiers with double underscores and allMembers

The identifer whitelist has been converted into a blacklist of all possible
internal D language declarations.

Reviewed-on: https://github.com/dlang/dmd/pull/10791

58 files changed:
gcc/d/dmd/MERGE
gcc/d/dmd/attrib.c
gcc/d/dmd/attrib.h
gcc/d/dmd/cond.c
gcc/d/dmd/cond.h
gcc/d/dmd/cppmangle.c
gcc/d/dmd/declaration.c
gcc/d/dmd/dinterpret.c
gcc/d/dmd/dmangle.c
gcc/d/dmd/dsymbol.c
gcc/d/dmd/dsymbol.h
gcc/d/dmd/expression.c
gcc/d/dmd/expression.h
gcc/d/dmd/expressionsem.c
gcc/d/dmd/func.c
gcc/d/dmd/hdrgen.c
gcc/d/dmd/init.c
gcc/d/dmd/intrange.c
gcc/d/dmd/json.c
gcc/d/dmd/mtype.c
gcc/d/dmd/mtype.h
gcc/d/dmd/parse.c
gcc/d/dmd/parse.h
gcc/d/dmd/scope.h
gcc/d/dmd/statement.c
gcc/d/dmd/statement.h
gcc/d/dmd/statementsem.c
gcc/d/dmd/traits.c
gcc/d/dmd/visitor.h
gcc/testsuite/gdc.test/compilable/b12001.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/json.d
gcc/testsuite/gdc.test/compilable/staticforeach.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test11169.d
gcc/testsuite/gdc.test/compilable/test17819.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test18871.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test7815.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test7886.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/e7804_1.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/e7804_2.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail11169.d [deleted file]
gcc/testsuite/gdc.test/fail_compilation/fail19182.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail19336.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail19520.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail2195.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail7815.d [deleted file]
gcc/testsuite/gdc.test/fail_compilation/fail7886.d [deleted file]
gcc/testsuite/gdc.test/fail_compilation/staticforeach1.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/staticforeach2.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/staticforeach3.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test17307.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/traits_alone.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/arrayop.d
gcc/testsuite/gdc.test/runnable/constfold.d
gcc/testsuite/gdc.test/runnable/e7804.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/imports/template13478a.d
gcc/testsuite/gdc.test/runnable/staticforeach.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test42.d
gcc/testsuite/gdc.test/runnable/traits.d

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