]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
d: Merge upstream dmd, druntime 28a3b24c2e, phobos 8ab95ded5.
authorIain Buclaw <ibuclaw@gdcproject.org>
Sun, 9 Jul 2023 20:08:36 +0000 (22:08 +0200)
committerIain Buclaw <ibuclaw@gdcproject.org>
Sun, 9 Jul 2023 20:08:36 +0000 (22:08 +0200)
D front-end changes:

- Import dmd v2.104.0-beta.1.
- Better error message when attribute inference fails down the
  call stack.
- Using `;' as an empty statement has been turned into an error.
- Using `in' parameters with non- `extern(D)' or `extern(C++)'
  functions is deprecated.
- `in ref' on parameters has been deprecated in favor of
  `-preview=in'.
- Throwing `immutable', `const', `inout', and `shared' qualified
  objects is now deprecated.
- User Defined Attributes now parse Template Arguments.

D runtime changes:

- Import druntime v2.104.0-beta.1.

Phobos changes:

- Import phobos v2.104.0-beta.1.
- Better static assert messages when instantiating
  `std.algorithm.comparison.clamp' with wrong inputs.
- `std.typecons.Rebindable' now supports all types.

gcc/d/ChangeLog:

* dmd/MERGE: Merge upstream dmd 28a3b24c2e.
* dmd/VERSION: Bump version to v2.104.0-beta.1.
* d-codegen.cc (build_bounds_slice_condition): Update for new
front-end interface.
* d-lang.cc (d_init_options): Likewise.
(d_handle_option): Likewise.
(d_post_options): Initialize global.compileEnv.
* expr.cc (ExprVisitor::visit (CatExp *)): Replace code generation
with new front-end lowering.
(ExprVisitor::visit (LoweredAssignExp *)): New method.
(ExprVisitor::visit (StructLiteralExp *)): Don't generate static
initializer symbols for structs defined in C sources.
* runtime.def (ARRAYCATT): Remove.
(ARRAYCATNTX): Remove.

libphobos/ChangeLog:

* libdruntime/MERGE: Merge upstream druntime 28a3b24c2e.
* src/MERGE: Merge upstream phobos 8ab95ded5.

gcc/testsuite/ChangeLog:

* gdc.dg/rtti1.d: Move array concat testcase to ...
* gdc.dg/nogc1.d: ... here.  New test.

190 files changed:
gcc/d/d-codegen.cc
gcc/d/d-lang.cc
gcc/d/dmd/MERGE
gcc/d/dmd/README.md
gcc/d/dmd/VERSION
gcc/d/dmd/aliasthis.d
gcc/d/dmd/apply.d
gcc/d/dmd/astenums.d
gcc/d/dmd/attrib.d
gcc/d/dmd/attrib.h
gcc/d/dmd/blockexit.d
gcc/d/dmd/canthrow.d
gcc/d/dmd/clone.d
gcc/d/dmd/common/string.d
gcc/d/dmd/constfold.d
gcc/d/dmd/cparse.d
gcc/d/dmd/cppmangle.d
gcc/d/dmd/ctfeexpr.d
gcc/d/dmd/dcast.d
gcc/d/dmd/declaration.d
gcc/d/dmd/declaration.h
gcc/d/dmd/dinterpret.d
gcc/d/dmd/dmodule.d
gcc/d/dmd/doc.d
gcc/d/dmd/dscope.d
gcc/d/dmd/dstruct.d
gcc/d/dmd/dsymbol.d
gcc/d/dmd/dsymbol.h
gcc/d/dmd/dsymbolsem.d
gcc/d/dmd/dtemplate.d
gcc/d/dmd/dtoh.d
gcc/d/dmd/errors.d
gcc/d/dmd/errorsink.d
gcc/d/dmd/escape.d
gcc/d/dmd/expression.d
gcc/d/dmd/expression.h
gcc/d/dmd/expressionsem.d
gcc/d/dmd/foreachvar.d
gcc/d/dmd/func.d
gcc/d/dmd/globals.d
gcc/d/dmd/globals.h
gcc/d/dmd/hdrgen.d
gcc/d/dmd/iasm.d
gcc/d/dmd/iasmgcc.d
gcc/d/dmd/id.d
gcc/d/dmd/init.d
gcc/d/dmd/initsem.d
gcc/d/dmd/json.d
gcc/d/dmd/lexer.d
gcc/d/dmd/mtype.d
gcc/d/dmd/nogc.d
gcc/d/dmd/ob.d
gcc/d/dmd/opover.d
gcc/d/dmd/optimize.d
gcc/d/dmd/parse.d
gcc/d/dmd/parsetimevisitor.d
gcc/d/dmd/printast.d
gcc/d/dmd/semantic3.d
gcc/d/dmd/sideeffect.d
gcc/d/dmd/statement.d
gcc/d/dmd/statement.h
gcc/d/dmd/statementsem.d
gcc/d/dmd/tokens.d
gcc/d/dmd/tokens.h
gcc/d/dmd/traits.d
gcc/d/dmd/transitivevisitor.d
gcc/d/dmd/typesem.d
gcc/d/dmd/typinf.d
gcc/d/dmd/visitor.d
gcc/d/dmd/visitor.h
gcc/d/expr.cc
gcc/d/runtime.def
gcc/testsuite/gdc.dg/nogc1.d [new file with mode: 0644]
gcc/testsuite/gdc.dg/rtti1.d
gcc/testsuite/gdc.test/compilable/dtoh_CPPNamespaceDeclaration.d
gcc/testsuite/gdc.test/compilable/dtoh_functions.d
gcc/testsuite/gdc.test/compilable/dtoh_invalid_identifiers.d
gcc/testsuite/gdc.test/compilable/dtoh_special_enum.d
gcc/testsuite/gdc.test/compilable/imports/c23789.i [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/interpret3.d
gcc/testsuite/gdc.test/compilable/test18493.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test19688.d [moved from gcc/testsuite/gdc.test/runnable/test19688.d with 100% similarity]
gcc/testsuite/gdc.test/compilable/test21667.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23789.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23862.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23863.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d
gcc/testsuite/gdc.test/compilable/user_defined_attributes.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/warn3882.d
gcc/testsuite/gdc.test/fail_compilation/attributediagnostic.d
gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_nogc.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_nothrow.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_pure.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/bug9631.d
gcc/testsuite/gdc.test/fail_compilation/ctfeblock.d
gcc/testsuite/gdc.test/fail_compilation/deprecatedinref.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/deprecations_preview_in.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/diag10319.d
gcc/testsuite/gdc.test/fail_compilation/diag10415.d
gcc/testsuite/gdc.test/fail_compilation/diag11769.d
gcc/testsuite/gdc.test/fail_compilation/diag14818.d
gcc/testsuite/gdc.test/fail_compilation/diag20268.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/diag8101b.d
gcc/testsuite/gdc.test/fail_compilation/diag9312.d
gcc/testsuite/gdc.test/fail_compilation/diag9620.d
gcc/testsuite/gdc.test/fail_compilation/diag9831.d
gcc/testsuite/gdc.test/fail_compilation/dip1000_deprecation.d
gcc/testsuite/gdc.test/fail_compilation/dtor_attributes.d
gcc/testsuite/gdc.test/fail_compilation/dtorfields_attributes.d
gcc/testsuite/gdc.test/fail_compilation/fail10968.d
gcc/testsuite/gdc.test/fail_compilation/fail11375.d
gcc/testsuite/gdc.test/fail_compilation/fail12236.d
gcc/testsuite/gdc.test/fail_compilation/fail13120.d
gcc/testsuite/gdc.test/fail_compilation/fail13577.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail16600.d
gcc/testsuite/gdc.test/fail_compilation/fail17518.d
gcc/testsuite/gdc.test/fail_compilation/fail17955.d
gcc/testsuite/gdc.test/fail_compilation/fail196.d
gcc/testsuite/gdc.test/fail_compilation/fail19948.d
gcc/testsuite/gdc.test/fail_compilation/fail20609.d
gcc/testsuite/gdc.test/fail_compilation/fail22202.d
gcc/testsuite/gdc.test/fail_compilation/fail23773.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail23822.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail23826.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail23861.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail332.d
gcc/testsuite/gdc.test/fail_compilation/fail4375q.d
gcc/testsuite/gdc.test/fail_compilation/fail4559.d
gcc/testsuite/gdc.test/fail_compilation/fail_typeof.d
gcc/testsuite/gdc.test/fail_compilation/ice10651.d
gcc/testsuite/gdc.test/fail_compilation/ice11626.d
gcc/testsuite/gdc.test/fail_compilation/ice11982.d
gcc/testsuite/gdc.test/fail_compilation/ice13225.d
gcc/testsuite/gdc.test/fail_compilation/ice23097.d
gcc/testsuite/gdc.test/fail_compilation/ice9540.d
gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
gcc/testsuite/gdc.test/fail_compilation/named_arguments_parse.d
gcc/testsuite/gdc.test/fail_compilation/parseStc.d
gcc/testsuite/gdc.test/fail_compilation/previewin.d
gcc/testsuite/gdc.test/fail_compilation/retscope2.d
gcc/testsuite/gdc.test/fail_compilation/retscope6.d
gcc/testsuite/gdc.test/fail_compilation/systemvariables_deprecation.d
gcc/testsuite/gdc.test/fail_compilation/testInference.d
gcc/testsuite/gdc.test/fail_compilation/testrvaluecpctor.d
gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/eh2.d
gcc/testsuite/gdc.test/runnable/imports/link11069z.d
gcc/testsuite/gdc.test/runnable/imports/link13415a.d
gcc/testsuite/gdc.test/runnable/imports/mainx23837.c [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/mangle.d
gcc/testsuite/gdc.test/runnable/test23837.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test42.d
gcc/testsuite/gdc.test/runnable/xtest46.d
libphobos/libdruntime/MERGE
libphobos/libdruntime/core/atomic.d
libphobos/libdruntime/core/demangle.d
libphobos/libdruntime/core/internal/array/concatenation.d
libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
libphobos/libdruntime/core/internal/string.d
libphobos/libdruntime/core/stdc/config.d
libphobos/libdruntime/core/sync/condition.d
libphobos/libdruntime/core/thread/package.d
libphobos/libdruntime/core/thread/types.d
libphobos/libdruntime/core/time.d
libphobos/libdruntime/gcc/sections/elf.d
libphobos/libdruntime/object.d
libphobos/libdruntime/rt/aApply.d
libphobos/libdruntime/rt/aApplyR.d
libphobos/libdruntime/rt/lifetime.d
libphobos/libdruntime/rt/profilegc.d
libphobos/src/MERGE
libphobos/src/std/algorithm/comparison.d
libphobos/src/std/algorithm/iteration.d
libphobos/src/std/algorithm/searching.d
libphobos/src/std/array.d
libphobos/src/std/concurrency.d
libphobos/src/std/container/dlist.d
libphobos/src/std/experimental/allocator/building_blocks/region.d
libphobos/src/std/file.d
libphobos/src/std/json.d
libphobos/src/std/net/curl.d
libphobos/src/std/path.d
libphobos/src/std/random.d
libphobos/src/std/range/package.d
libphobos/src/std/range/primitives.d
libphobos/src/std/regex/internal/backtracking.d
libphobos/src/std/regex/internal/thompson.d
libphobos/src/std/stdio.d
libphobos/src/std/traits.d
libphobos/src/std/typecons.d

index 9bae06077b5be06dbd5ee6fa6d58f9869c71734b..689d1c5a0d4e72197e4056dbb0e51d75e3e2d0d5 100644 (file)
@@ -1986,14 +1986,14 @@ build_bounds_slice_condition (SliceExp *se, tree lower, tree upper, tree length)
       tree condition = NULL_TREE;
 
       /* Enforces that `upper <= length`.  */
-      if (!se->upperIsInBounds && length != NULL_TREE)
+      if (!se->upperIsInBounds () && length != NULL_TREE)
        condition = fold_build2 (GT_EXPR, d_bool_type, upper, length);
       else
        length = integer_zero_node;
 
       /* Enforces that `lower <= upper`.  No need to check `lower <= length` as
         we've already ensured that `upper <= length`.  */
-      if (!se->lowerIsLessThanUpper)
+      if (!se->lowerIsLessThanUpper ())
        {
          tree lwr_cond = fold_build2 (GT_EXPR, d_bool_type, lower, upper);
 
index 235e22a4b41e32a23741fe87ddd4981a3fdf5903..7cb86bf268ff10eb532f20437b81d9486ed494c9 100644 (file)
@@ -293,7 +293,7 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options)
   /* Set default values.  */
   global._init ();
 
-  global.vendor = lang_hooks.name;
+  global.compileEnv.vendor = lang_hooks.name;
   global.params.argv0 = xstrdup (decoded_options[0].arg);
   global.params.errorLimit = flag_max_errors;
 
@@ -562,7 +562,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       global.params.useDIP1021 = value;
       global.params.bitfields = value;
       global.params.dtorFields = FeatureState::enabled;
-      global.params.fieldwise = value;
+      global.params.fieldwise = FeatureState::enabled;
       global.params.fixAliasThis = value;
       global.params.previewIn = value;
       global.params.fix16997 = value;
@@ -594,7 +594,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_fpreview_fieldwise:
-      global.params.fieldwise = value;
+      global.params.fieldwise = FeatureState::enabled;
       break;
 
     case OPT_fpreview_fixaliasthis:
@@ -934,6 +934,12 @@ d_post_options (const char ** fn)
 
   global.params.obj = !flag_syntax_only;
 
+  /* The front-end parser only has access to `compileEnv', synchronize its
+     fields with params.  */
+  global.compileEnv.previewIn = global.params.previewIn;
+  global.compileEnv.ddocOutput = global.params.ddoc.doOutput;
+  global.compileEnv.shortenedMethods = global.params.shortenedMethods;
+
   /* Add in versions given on the command line.  */
   if (global.params.versionids)
     {
index 1205cd941b79971fbfc314401c91cc16b43d9abb..95ea67d789bd245c92b0cf0d12a4212fa1e926a6 100644 (file)
@@ -1,4 +1,4 @@
-a45f4e9f43e9fdbf0b666175e5e66b1ce4f561f6
+28a3b24c2e45de39cd3df528142fd06b6456e8fd
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
index cecd008e608b519e765df8bf614930aa1aeb2b43..57f56f3f0f0f03d3268c85ecbfa3f6f27b49c737 100644 (file)
@@ -19,7 +19,7 @@ this license for that file.
 | Folder                                                                   | Purpose                                                                                                                                                                                                       |
 |--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | [dmd/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd)                 | The dmd driver and front-end                                                                                                                                                                                  |
-| [dmd/backend/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). |
+| [dmd/backend/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/backend) | Code generation for x86 or x86-64. Based on [DMC](https://github.com/DigitalMars/Compiler/)'s backend, but not kept in sync anymore. Not used by [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). |
 | [dmd/common/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/common)   | Code shared by the front-end and back-end                                                                                                                                                                     |
 | [dmd/root/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/root)       | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516).                                    |
 
index 8316aafdaca137c6c1e3adb2230999157681f06e..7cf912726b7809e8415c2db7f0d5f5e5c35b9adb 100644 (file)
@@ -1 +1 @@
-v2.103.1
+v2.104.0-beta.1
index ef839fae536aabea302c4d55fc7fb34b4cf5fe0a..ce384593c590209fbbfbbade6f80850feeea90c8 100644 (file)
@@ -78,7 +78,7 @@ extern (C++) final class AliasThis : Dsymbol
  * Params:
  *      sc = context
  *      e = expression forming the `this`
- *      gag = if true do not print errors, return null instead
+ *      gag = do not print errors, return `null` instead
  *      findOnly = don't do further processing like resolving properties,
  *                 i.e. just return plain dotExp() result.
  * Returns:
@@ -93,7 +93,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find
         {
             Loc loc = e.loc;
             Type tthis = (e.op == EXP.type ? e.type : null);
-            const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0);
+            const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
             uint olderrors = gag ? global.startGagging() : 0;
             e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags);
             if (!e || findOnly)
@@ -200,15 +200,29 @@ bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc)
 
 /**************************************
  * Check and set 'att' if 't' is a recursive 'alias this' type
+ *
+ * The goal is to prevent endless loops when there is a cycle in the alias this chain.
+ * Since there is no multiple `alias this`, the chain either ends in a leaf,
+ * or it loops back on itself as some point.
+ *
+ * Example: S0 -> (S1 -> S2 -> S3 -> S1)
+ *
+ * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried.
+ * `S1` is a recursive alias this type, but since `att` is initialized to `null`,
+ * this still returns `false`, but `att1` is set to `S1`.
+ * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again,
+ * we notice `att == t`, so we're back at the start of the loop, and this returns `true`.
+ *
  * Params:
- *   att = type reference used to detect recursion
- *   t   = 'alias this' type
+ *   att = type reference used to detect recursion. Should be initialized to `null`.
+ *   t   = type of 'alias this' rewrite to attempt
  *
  * Returns:
- *   Whether the 'alias this' is recursive or not
+ *   `false` if the rewrite is safe, `true` if it would loop back around
  */
 bool isRecursiveAliasThis(ref Type att, Type t)
 {
+    //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars());
     auto tb = t.toBasetype();
     if (att && tb.equivalent(att))
         return true;
index 59ba9f5ecd65fb815c8bde7b5d2f0bc401d8b720..d18b81f044fbde29417b7af7ecbc240b93895883 100644 (file)
@@ -170,7 +170,7 @@ public:
     {
         if (e.stageflags & stageApply)
             return;
-        int old = e.stageflags;
+        const old = e.stageflags;
         e.stageflags |= stageApply;
         doCond(e.elements.peekSlice()) || applyTo(e);
         e.stageflags = old;
index 6e882082bed9581de19f45641bd8c7b52c34ba7f..77f36f304a57b4e59a5abe265fda0192a877b9f3 100644 (file)
@@ -214,8 +214,8 @@ enum TY : ubyte
     Tmixin,
     Tnoreturn,
     Ttag,
-    TMAX
 }
+enum TMAX = TY.max + 1;
 
 alias Tarray = TY.Tarray;
 alias Tsarray = TY.Tsarray;
@@ -265,7 +265,6 @@ alias Ttraits = TY.Ttraits;
 alias Tmixin = TY.Tmixin;
 alias Tnoreturn = TY.Tnoreturn;
 alias Ttag = TY.Ttag;
-alias TMAX = TY.TMAX;
 
 enum TFlags
 {
@@ -328,6 +327,7 @@ enum VarArg : ubyte
     variadic = 1,  /// (T t, ...)  can be C-style (core.stdc.stdarg) or D-style (core.vararg)
     typesafe = 2,  /// (T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions
                    ///   or https://dlang.org/spec/function.html#typesafe_variadic_functions
+    KRvariadic = 3, /// K+R C style variadics (no function prototype)
 }
 
 /*************************
@@ -339,7 +339,7 @@ enum STMT : ubyte
     Error,
     Peel,
     Exp, DtorExp,
-    Compile,
+    Mixin,
     Compound, CompoundDeclaration, CompoundAsm,
     UnrolledLoop,
     Scope,
@@ -439,3 +439,22 @@ enum FileType : ubyte
     ddoc, /// Ddoc documentation file (.dd)
     c,    /// C source file
 }
+
+extern (C++) struct structalign_t
+{
+  private:
+    ushort value = 0;  // unknown
+    enum STRUCTALIGN_DEFAULT = 1234;   // default = match whatever the corresponding C compiler does
+    bool pack;         // use #pragma pack semantics
+
+  public:
+  pure @safe @nogc nothrow:
+    bool isDefault() const { return value == STRUCTALIGN_DEFAULT; }
+    void setDefault()      { value = STRUCTALIGN_DEFAULT; }
+    bool isUnknown() const { return value == 0; }  // value is not set
+    void setUnknown()      { value = 0; }
+    void set(uint value)   { this.value = cast(ushort)value; }
+    uint get() const       { return value; }
+    bool isPack() const    { return pack; }
+    void setPack(bool pack) { this.pack = pack; }
+}
index dbe78ef74bc595bb61c3d5448dbbb2b859bbcdee..c08382cbd045f9fe34fa20263eef9fb733021206 100644 (file)
@@ -62,6 +62,12 @@ extern (C++) abstract class AttribDeclaration : Dsymbol
         this.decl = decl;
     }
 
+    extern (D) this(const ref Loc loc, Dsymbols* decl)
+    {
+        super(loc, null);
+        this.decl = decl;
+    }
+
     extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl)
     {
         super(loc, ident);
@@ -228,6 +234,12 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration
         this.stc = stc;
     }
 
+    extern (D) this(const ref Loc loc, StorageClass stc, Dsymbols* decl)
+    {
+        super(loc, decl);
+        this.stc = stc;
+    }
+
     override StorageClassDeclaration syntaxCopy(Dsymbol s)
     {
         assert(!s);
@@ -1279,7 +1291,8 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration
  *      mixin("int x");
  * https://dlang.org/spec/module.html#mixin-declaration
  */
-extern (C++) final class CompileDeclaration : AttribDeclaration
+// Note: was CompileDeclaration
+extern (C++) final class MixinDeclaration : AttribDeclaration
 {
     Expressions* exps;
     ScopeDsymbol scopesym;
@@ -1288,19 +1301,19 @@ extern (C++) final class CompileDeclaration : AttribDeclaration
     extern (D) this(const ref Loc loc, Expressions* exps)
     {
         super(loc, null, null);
-        //printf("CompileDeclaration(loc = %d)\n", loc.linnum);
+        //printf("MixinDeclaration(loc = %d)\n", loc.linnum);
         this.exps = exps;
     }
 
-    override CompileDeclaration syntaxCopy(Dsymbol s)
+    override MixinDeclaration syntaxCopy(Dsymbol s)
     {
-        //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars());
-        return new CompileDeclaration(loc, Expression.arraySyntaxCopy(exps));
+        //printf("MixinDeclaration::syntaxCopy('%s')\n", toChars());
+        return new MixinDeclaration(loc, Expression.arraySyntaxCopy(exps));
     }
 
     override void addMember(Scope* sc, ScopeDsymbol sds)
     {
-        //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum);
+        //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum);
         this.scopesym = sds;
     }
 
@@ -1314,7 +1327,7 @@ extern (C++) final class CompileDeclaration : AttribDeclaration
         return "mixin";
     }
 
-    override inout(CompileDeclaration) isCompileDeclaration() inout
+    override inout(MixinDeclaration) isMixinDeclaration() inout
     {
         return this;
     }
index 113653e9d7c8f06c875a145165858ea44482b58b..1e75598c72ad25dca41e862a62b3f1c94c56d422 100644 (file)
@@ -221,7 +221,7 @@ public:
 
 // Mixin declarations
 
-class CompileDeclaration final : public AttribDeclaration
+class MixinDeclaration final : public AttribDeclaration
 {
 public:
     Expressions *exps;
@@ -229,7 +229,7 @@ public:
     ScopeDsymbol *scopesym;
     d_bool compiled;
 
-    CompileDeclaration *syntaxCopy(Dsymbol *s) override;
+    MixinDeclaration *syntaxCopy(Dsymbol *s) override;
     void addMember(Scope *sc, ScopeDsymbol *sds) override;
     void setScope(Scope *sc) override;
     const char *kind() const override;
index bd5b78e92dc557817f647589e56a60c8b4fd53a3..db738b4076c64e2c71ff43f8f7ec49fe6ddb3a0c 100644 (file)
@@ -63,34 +63,21 @@ enum BE : int
  */
 int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
 {
-    extern (C++) final class BlockExit : Visitor
-    {
-        alias visit = Visitor.visit;
-    public:
-        FuncDeclaration func;
-        bool mustNotThrow;
-        int result;
-
-        extern (D) this(FuncDeclaration func, bool mustNotThrow) scope
-        {
-            this.func = func;
-            this.mustNotThrow = mustNotThrow;
-            result = BE.none;
-        }
+        int result = BE.none;
 
-        override void visit(Statement s)
+        void visitDefaultCase(Statement s)
         {
             printf("Statement::blockExit(%p)\n", s);
             printf("%s\n", s.toChars());
             assert(0);
         }
 
-        override void visit(ErrorStatement s)
+        void visitError(ErrorStatement s)
         {
             result = BE.none;
         }
 
-        override void visit(ExpStatement s)
+        void visitExp(ExpStatement s)
         {
             result = BE.fallthru;
             if (s.exp)
@@ -115,13 +102,18 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             }
         }
 
-        override void visit(CompileStatement s)
+        void visitDtorExp(DtorExpStatement s)
+        {
+            visitExp(s);
+        }
+
+        void visitMixin(MixinStatement s)
         {
             assert(global.errors);
             result = BE.fallthru;
         }
 
-        override void visit(CompoundStatement cs)
+        void visitCompound(CompoundStatement cs)
         {
             //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.length, result);
             result = BE.fallthru;
@@ -175,7 +167,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             }
         }
 
-        override void visit(UnrolledLoopStatement uls)
+        void visitUnrolledLoop(UnrolledLoopStatement uls)
         {
             result = BE.fallthru;
             foreach (s; *uls.statements)
@@ -190,19 +182,19 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             }
         }
 
-        override void visit(ScopeStatement s)
+        void visitScope(ScopeStatement s)
         {
             //printf("ScopeStatement::blockExit(%p)\n", s.statement);
             result = blockExit(s.statement, func, mustNotThrow);
         }
 
-        override void visit(WhileStatement s)
+        void visitWhile(WhileStatement s)
         {
             assert(global.errors);
             result = BE.fallthru;
         }
 
-        override void visit(DoStatement s)
+        void visitDo(DoStatement s)
         {
             if (s._body)
             {
@@ -227,7 +219,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             result &= ~(BE.break_ | BE.continue_);
         }
 
-        override void visit(ForStatement s)
+        void visitFor(ForStatement s)
         {
             result = BE.fallthru;
             if (s._init)
@@ -259,7 +251,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
                 result |= canThrow(s.increment, func, mustNotThrow);
         }
 
-        override void visit(ForeachStatement s)
+        void visitForeach(ForeachStatement s)
         {
             result = BE.fallthru;
             result |= canThrow(s.aggr, func, mustNotThrow);
@@ -268,13 +260,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
                 result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_);
         }
 
-        override void visit(ForeachRangeStatement s)
+        void visitForeachRange(ForeachRangeStatement s)
         {
             assert(global.errors);
             result = BE.fallthru;
         }
 
-        override void visit(IfStatement s)
+        void visitIf(IfStatement s)
         {
             //printf("IfStatement::blockExit(%p)\n", s);
             result = BE.none;
@@ -297,24 +289,24 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             //printf("IfStatement::blockExit(%p) = x%x\n", s, result);
         }
 
-        override void visit(ConditionalStatement s)
+        void visitConditional(ConditionalStatement s)
         {
             result = blockExit(s.ifbody, func, mustNotThrow);
             if (s.elsebody)
                 result |= blockExit(s.elsebody, func, mustNotThrow);
         }
 
-        override void visit(PragmaStatement s)
+        void visitPragma(PragmaStatement s)
         {
             result = BE.fallthru;
         }
 
-        override void visit(StaticAssertStatement s)
+        void visitStaticAssert(StaticAssertStatement s)
         {
             result = BE.fallthru;
         }
 
-        override void visit(SwitchStatement s)
+        void visitSwitch(SwitchStatement s)
         {
             result = BE.none;
             result |= canThrow(s.condition, func, mustNotThrow);
@@ -332,63 +324,63 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
                 result |= BE.fallthru;
         }
 
-        override void visit(CaseStatement s)
+        void visitCase(CaseStatement s)
         {
             result = blockExit(s.statement, func, mustNotThrow);
         }
 
-        override void visit(DefaultStatement s)
+        void visitDefault(DefaultStatement s)
         {
             result = blockExit(s.statement, func, mustNotThrow);
         }
 
-        override void visit(GotoDefaultStatement s)
+        void visitGotoDefault(GotoDefaultStatement s)
         {
             result = BE.goto_;
         }
 
-        override void visit(GotoCaseStatement s)
+        void visitGotoCase(GotoCaseStatement s)
         {
             result = BE.goto_;
         }
 
-        override void visit(SwitchErrorStatement s)
+        void visitSwitchError(SwitchErrorStatement s)
         {
             // Switch errors are non-recoverable
             result = BE.halt;
         }
 
-        override void visit(ReturnStatement s)
+        void visitReturn(ReturnStatement s)
         {
             result = BE.return_;
             if (s.exp)
                 result |= canThrow(s.exp, func, mustNotThrow);
         }
 
-        override void visit(BreakStatement s)
+        void visitBreak(BreakStatement s)
         {
             //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_);
             result = s.ident ? BE.goto_ : BE.break_;
         }
 
-        override void visit(ContinueStatement s)
+        void visitContinue(ContinueStatement s)
         {
             result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_;
         }
 
-        override void visit(SynchronizedStatement s)
+        void visitSynchronized(SynchronizedStatement s)
         {
             result = blockExit(s._body, func, mustNotThrow);
         }
 
-        override void visit(WithStatement s)
+        void visitWith(WithStatement s)
         {
             result = BE.none;
             result |= canThrow(s.exp, func, mustNotThrow);
             result |= blockExit(s._body, func, mustNotThrow);
         }
 
-        override void visit(TryCatchStatement s)
+        void visitTryCatch(TryCatchStatement s)
         {
             assert(s._body);
             result = blockExit(s._body, func, false);
@@ -428,7 +420,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             result |= catchresult;
         }
 
-        override void visit(TryFinallyStatement s)
+        void visitTryFinally(TryFinallyStatement s)
         {
             result = BE.fallthru;
             if (s._body)
@@ -470,13 +462,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             result |= finalresult & ~BE.fallthru;
         }
 
-        override void visit(ScopeGuardStatement s)
+        void visitScopeGuard(ScopeGuardStatement s)
         {
             // At this point, this statement is just an empty placeholder
             result = BE.fallthru;
         }
 
-        override void visit(ThrowStatement s)
+        void visitThrow(ThrowStatement s)
         {
             if (s.internalThrow)
             {
@@ -486,16 +478,16 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
                 return;
             }
 
-            result = checkThrow(s.loc, s.exp, mustNotThrow);
+            result = checkThrow(s.loc, s.exp, mustNotThrow, func);
         }
 
-        override void visit(GotoStatement s)
+        void visitGoto(GotoStatement s)
         {
             //printf("GotoStatement::blockExit(%p)\n", s);
             result = BE.goto_;
         }
 
-        override void visit(LabelStatement s)
+        void visitLabel(LabelStatement s)
         {
             //printf("LabelStatement::blockExit(%p)\n", s);
             result = blockExit(s.statement, func, mustNotThrow);
@@ -503,30 +495,31 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
                 result |= BE.fallthru;
         }
 
-        override void visit(CompoundAsmStatement s)
+        void visitCompoundAsm(CompoundAsmStatement s)
         {
             // Assume the worst
             result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt;
             if (!(s.stc & STC.nothrow_))
             {
-                if (mustNotThrow && !(s.stc & STC.nothrow_))
-                    s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not");
+                if(func)
+                    func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not");
+                if (mustNotThrow)
+                    s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO
                 else
                     result |= BE.throw_;
             }
         }
 
-        override void visit(ImportStatement s)
+        void visitImport(ImportStatement s)
         {
             result = BE.fallthru;
         }
-    }
 
     if (!s)
         return BE.fallthru;
-    scope BlockExit be = new BlockExit(func, mustNotThrow);
-    s.accept(be);
-    return be.result;
+    mixin VisitStatement!void visit;
+    visit.VisitStatement(s);
+    return result;
 }
 
 /++
@@ -537,10 +530,11 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
  +   loc          = location of the `throw`
  +   exp          = expression yielding the throwable
  +   mustNotThrow = inside of a `nothrow` scope?
+ +   func         = function containing the `throw`
  +
  + Returns: `BE.[err]throw` depending on the type of `exp`
  +/
-BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow)
+BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow, FuncDeclaration func)
 {
     import dmd.errors : error;
 
@@ -554,6 +548,8 @@ BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow)
     }
     if (mustNotThrow)
         loc.error("`%s` is thrown but not caught", exp.type.toChars());
+    else if (func)
+        func.setThrow(loc, "`%s` is thrown but not caught", exp.type);
 
     return BE.throw_;
 }
index 0c237e6da567015bc5ac14b68a8337daafbac2fb..7dfec8a043abd4996516affe466c4f1358d04bbc 100644 (file)
@@ -53,7 +53,7 @@ enum CT : BE
  */
 extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
 {
-    //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
+    //printf("Expression::canThrow(%d) %s\n", mustNotThrow, e.toChars());
     // stop walking if we determine this expression can throw
     extern (C++) final class CanThrow : StoppableVisitor
     {
@@ -76,11 +76,16 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN
             {
                 if (mustNotThrow)
                 {
-                    e.error("%s `%s` is not `nothrow`",
-                        f.kind(), f.toPrettyChars());
+                    e.error("%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars());
+                    if (!f.isDtorDeclaration())
+                        errorSupplementalInferredAttr(f, 10, false, STC.nothrow_);
 
                     e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
                 }
+                else if (func)
+                {
+                    func.setThrowCall(e.loc, f);
+                }
                 result |= CT.exception;
             }
         }
@@ -205,7 +210,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN
 
         override void visit(ThrowExp te)
         {
-            const res = checkThrow(te.loc, te.e1, mustNotThrow);
+            const res = checkThrow(te.loc, te.e1, mustNotThrow, func);
             assert((res & ~(CT.exception | CT.error)) == 0);
             result |= res;
         }
index 19bf83e4ec353144df64fc98b580e39950a4f102..60e373c502b9b7fcdee824381202cd72ef385e54 100644 (file)
@@ -840,7 +840,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
         "    else " ~
         "        h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
         "return h;";
-    fop.fbody = new CompileStatement(loc, new StringExp(loc, code));
+    fop.fbody = new MixinStatement(loc, new StringExp(loc, code));
     Scope* sc2 = sc.push();
     sc2.stc = 0;
     sc2.linkage = LINK.d;
@@ -1261,8 +1261,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
 
         // if this field's postblit is not `nothrow`, add a `scope(failure)`
         // block to destroy any prior successfully postblitted fields should
-        // this field's postblit fail
-        if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow)
+        // this field's postblit fail.
+        // Don't generate it for betterC code since it cannot throw exceptions.
+        if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && !global.params.betterC)
         {
              // create a list of destructors that need to be called
             Expression[] dtorCalls;
index 1111cec2cf1ea8a614cdf56555d77eb45117e629..a1614fd907c8b0e864f140d1c5d64ddf0ec4e56c 100644 (file)
@@ -13,7 +13,7 @@ module dmd.common.string;
 nothrow:
 
 /**
-Defines a temporary array using a fixed-length buffer as back store. If the length
+Defines a temporary array of `Element`s using a fixed-length buffer as back store. If the length
 of the buffer suffices, it is readily used. Otherwise, `malloc` is used to
 allocate memory for the array and `free` is used for deallocation in the
 destructor.
@@ -21,19 +21,26 @@ destructor.
 This type is meant to use exclusively as an automatic variable. It is not
 default constructible or copyable.
 */
-struct SmallBuffer(T)
+struct SmallBuffer(Element)
 {
     import core.stdc.stdlib : malloc, free;
 
-    private T[] _extent;
+    private Element[] _extent;
     private bool needsFree;
 
   nothrow:
+  @nogc:
 
     @disable this(); // no default ctor
-    @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable
-
-    this(size_t len, T[] buffer)
+    @disable this(ref const SmallBuffer!Element); // noncopyable, nonassignable
+
+    /***********
+     * Construct a SmallBuffer
+     * Params:
+     *  len = number of elements in array
+     *  buffer = slice to use as backing-store, if len will fit in it
+     */
+    scope this(size_t len, return scope Element[] buffer)
     {
         if (len <= buffer.length)
         {
@@ -41,7 +48,8 @@ struct SmallBuffer(T)
         }
         else
         {
-            _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len];
+            assert(len < sizeof.max / Element.sizeof);
+            _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len];
             _extent.ptr || assert(0, "Out of memory.");
             needsFree = true;
         }
@@ -54,16 +62,22 @@ struct SmallBuffer(T)
             free(_extent.ptr);
     }
 
-    void create(size_t len)
+    /******
+     * Resize existing SmallBuffer.
+     * Params:
+     *  len = number of elements after resize
+     */
+    scope void create(size_t len)
     {
         if (len <= _extent.length)
         {
-            _extent = _extent[0 .. len];
+            _extent = _extent[0 .. len]; // reuse existing storage
         }
         else
         {
             __dtor();
-            _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len];
+            assert(len < sizeof.max / Element.sizeof);
+            _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len];
             _extent.ptr || assert(0, "Out of memory.");
             needsFree = true;
         }
index e4be63cda3e5261122c2f24644e72174eefd9061..415606bd704e6dcf94477a3bab3ea241e4fa51c9 100644 (file)
@@ -1437,7 +1437,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
             emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
             StringExp es = ue.exp().isStringExp();
             es.type = type;
-            es.committed = 1;
+            es.committed = true;
         }
         else
         {
index a18f810372df477fb0ecc2d89d6e116b9f9446f0..9b7db1f33f41f4fa0407f00f9d2382d63ee7f63d 100644 (file)
 module dmd.cparse;
 
 import core.stdc.stdio;
-import core.stdc.string;
+import core.stdc.string : memcpy;
+
 import dmd.astenums;
 import dmd.errorsink;
-import dmd.globals;
 import dmd.id;
 import dmd.identifier;
 import dmd.lexer;
 import dmd.location;
 import dmd.parse;
-import dmd.errors;
 import dmd.root.array;
-import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.rootobject;
-import dmd.root.string;
 import dmd.tokens;
 
 /***********************************************************
@@ -71,9 +67,10 @@ final class CParser(AST) : Parser!AST
 
     extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment,
                             ErrorSink errorSink,
-                            const ref TARGET target, OutBuffer* defines) scope
+                            const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope
     {
-        super(_module, input, doDocComment, errorSink);
+        const bool doUnittests = false;
+        super(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
 
         //printf("CParser.this()\n");
         mod = _module;
@@ -202,7 +199,7 @@ final class CParser(AST) : Parser!AST
                     else if (token.value == TOK.leftCurly)
                         s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
                     else
-                        s = cparseStatement(ParseStatementFlags.semiOk);
+                        s = cparseStatement(0);
                     s = new AST.LabelStatement(loc, ident, s);
                     break;
                 }
@@ -307,6 +304,7 @@ final class CParser(AST) : Parser!AST
         case TOK.extern_:
         case TOK.static_:
         case TOK._Thread_local:
+        case TOK.__thread:
         case TOK.auto_:
         case TOK.register:
 
@@ -376,7 +374,7 @@ final class CParser(AST) : Parser!AST
             auto statements = new AST.Statements();
             while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
             {
-                statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+                statements.push(cparseStatement(ParseStatementFlags.curlyScope));
             }
             if (endPtr)
                 *endPtr = token.ptr;
@@ -510,6 +508,14 @@ final class CParser(AST) : Parser!AST
 
             nextToken();
             auto exp = cparseAssignExp();
+            AST.Expression expHigh;
+            if (token.value == TOK.dotDotDot)
+            {
+                /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
+                 */
+                nextToken();
+                expHigh = cparseAssignExp();
+            }
             check(TOK.colon);
 
             if (flags & ParseStatementFlags.curlyScope)
@@ -517,7 +523,7 @@ final class CParser(AST) : Parser!AST
                 auto statements = new AST.Statements();
                 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
                 {
-                    auto cur = cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
+                    auto cur = cparseStatement(ParseStatementFlags.curlyScope);
                     statements.push(cur);
 
                     // https://issues.dlang.org/show_bug.cgi?id=21739
@@ -532,10 +538,13 @@ final class CParser(AST) : Parser!AST
             }
             else
             {
-                s = cparseStatement(ParseStatementFlags.semi);
+                s = cparseStatement(0);
             }
             s = new AST.ScopeStatement(loc, s, token.loc);
-            s = new AST.CaseStatement(loc, exp, s);
+            if (expHigh)
+                s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
+            else
+                s = new AST.CaseStatement(loc, exp, s);
             break;
         }
 
@@ -549,12 +558,12 @@ final class CParser(AST) : Parser!AST
                 auto statements = new AST.Statements();
                 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
                 {
-                    statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+                    statements.push(cparseStatement(ParseStatementFlags.curlyScope));
                 }
                 s = new AST.CompoundStatement(loc, statements);
             }
             else
-                s = cparseStatement(ParseStatementFlags.semi);
+                s = cparseStatement(0);
             s = new AST.ScopeStatement(loc, s, token.loc);
             s = new AST.DefaultStatement(loc, s);
             break;
@@ -604,7 +613,20 @@ final class CParser(AST) : Parser!AST
         }
 
         case TOK.asm_:
-            s = parseAsm();
+            switch (peekNext())
+            {
+                case TOK.goto_:
+                case TOK.inline:
+                case TOK.volatile:
+                case TOK.leftParenthesis:
+                    s = cparseGnuAsm();
+                    break;
+
+                default:
+                    // ImportC extensions: parse as a D asm block.
+                    s = parseAsm();
+                    break;
+            }
             break;
 
         default:
@@ -777,7 +799,10 @@ final class CParser(AST) : Parser!AST
 
         case TOK.leftParenthesis:
             nextToken();
-            e = cparseExpression();
+            if (token.value == TOK.leftCurly)
+                e = cparseStatementExpression();        // gcc extension
+            else
+                e = cparseExpression();
             check(TOK.rightParenthesis);
             break;
 
@@ -1600,6 +1625,41 @@ final class CParser(AST) : Parser!AST
         return e;
     }
 
+    /*****************************
+     * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
+     * Represent as a function literal, then call the function literal.
+     * Parser is on opening curly brace.
+     */
+    private AST.Expression cparseStatementExpression()
+    {
+        AST.ParameterList parameterList;
+        StorageClass stc = 0;
+        const loc = token.loc;
+        typedefTab.push(null);
+        auto fbody = cparseStatement(ParseStatementFlags.scope_);
+        typedefTab.pop();                                        // end of function scope
+
+        // Rewrite last ExpStatement (if there is one) as a ReturnStatement
+        auto ss = fbody.isScopeStatement();
+        auto cs = ss.statement.isCompoundStatement();
+        assert(cs);
+        if (const len = (*cs.statements).length)
+        {
+            auto s = (*cs.statements)[len - 1];
+            if (auto es = s.isExpStatement())
+                (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
+        }
+
+        auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
+        auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
+        fd.fbody = fbody;
+
+        auto fe = new AST.FuncExp(loc, fd);
+        auto args = new AST.Expressions();
+        auto e = new AST.CallExp(loc, fe, args);   // call the function literal
+        return e;
+    }
+
     //}
     /********************************************************************************/
     /********************************* Declaration Parser ***************************/
@@ -1660,6 +1720,12 @@ final class CParser(AST) : Parser!AST
             auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
                         (tt.tok == TOK.union_)  ? new AST.UnionDeclaration(tt.loc, tt.id) :
                                                   new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
+            if (!tt.packalign.isUnknown())
+            {
+                // saw `struct __declspec(align(N)) Tag ...`
+                auto st = stag.isStructDeclaration();
+                st.alignment = tt.packalign;
+            }
             stag.members = tt.members;
             tt.members = null;
             if (!symbols)
@@ -1759,7 +1825,7 @@ final class CParser(AST) : Parser!AST
                 case TOK.asm_:
                 case TOK.__attribute__:
                     if (token.value == TOK.asm_)
-                        asmName = cparseSimpleAsmExpr();
+                        asmName = cparseGnuAsmLabel();
                     if (token.value == TOK.__attribute__)
                     {
                         cparseGnuAttributes(specifier);
@@ -1892,6 +1958,7 @@ final class CParser(AST) : Parser!AST
                     if (specifier.scw & SCW.x_Thread_local)
                         error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
                     auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn);
+                    specifiersToFuncDeclaration(fd, specifier);
                     s = fd;
                 }
                 else
@@ -1901,7 +1968,9 @@ final class CParser(AST) : Parser!AST
                     if (!hasInitializer &&
                         !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
                         initializer = new AST.VoidInitializer(token.loc);
-                    s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
+                    auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
+                    specifiersToVarDeclaration(vd, specifier);
+                    s = vd;
                 }
                 if (level != LVL.global)
                     insertIdToTypedefTab(id);   // non-typedef declarations can hide typedefs in outer scopes
@@ -2012,8 +2081,7 @@ final class CParser(AST) : Parser!AST
             auto pl = ft.parameterList;
             if (pl.varargs != AST.VarArg.none && pl.length)
                 error("function identifier-list cannot end with `...`");
-            ft.parameterList.varargs = AST.VarArg.variadic;     // but C11 allows extra arguments
-            importBuiltins = true;                              // will need __va_list_tag
+            ft.parameterList.varargs = AST.VarArg.KRvariadic;   // but C11 allows extra arguments
             auto plLength = pl.length;
             if (symbols.length != plLength)
                 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
@@ -2038,6 +2106,10 @@ final class CParser(AST) : Parser!AST
                     error("storage class and type are not allowed in identifier-list");
                 foreach (s; (*symbols)[]) // yes, quadratic
                 {
+                    auto ad = s.isAttribDeclaration();
+                    if (ad)
+                        s = (*ad.decl)[0];      // AlignDeclaration wrapping the declaration
+
                     auto d = s.isDeclaration();
                     if (d && p.ident == d.ident && d.type)
                     {
@@ -2062,6 +2134,7 @@ final class CParser(AST) : Parser!AST
         typedefTab.pop();                                        // end of function scope
 
         auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn);
+        specifiersToFuncDeclaration(fd, specifier);
 
         if (addFuncName)
         {
@@ -2234,6 +2307,7 @@ final class CParser(AST) : Parser!AST
                 case TOK.typedef_:   scwx = SCW.xtypedef;   break;
                 case TOK.inline:     scwx = SCW.xinline;    break;
                 case TOK._Noreturn:  scwx = SCW.x_Noreturn; break;
+                case TOK.__thread:
                 case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
 
                 // Type qualifiers
@@ -2269,15 +2343,23 @@ final class CParser(AST) : Parser!AST
                     const sloc = token.loc;
                     nextToken();
 
+                    Specifier tagSpecifier;
+
                     /* GNU Extensions
                      * struct-or-union-specifier:
                      *    struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
                      *    struct-or-union gnu-attribute (opt) identifier
                      */
-                    if (token.value == TOK.__attribute__)
-                        cparseGnuAttributes(specifier);
-
-                    t = cparseStruct(sloc, structOrUnion, symbols);
+                    while (1)
+                    {
+                        if (token.value == TOK.__attribute__)
+                            cparseGnuAttributes(tagSpecifier);
+                        else if (token.value == TOK.__declspec)
+                            cparseDeclspec(tagSpecifier);
+                        else
+                            break;
+                    }
+                    t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols);
                     tkwx = TKW.xtag;
                     break;
                 }
@@ -2297,9 +2379,9 @@ final class CParser(AST) : Parser!AST
                         tk = peek(tk);
                         if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
                         {
+                            nextToken();
                             nextToken();
                             t = cparseTypeName();
-                            // TODO - implement the "atomic" part of t
                             tkwx = TKW.x_Atomic;
                             break;
                         }
@@ -2468,6 +2550,12 @@ final class CParser(AST) : Parser!AST
                     error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
                     scw &= ~scwx;
                 }
+                if (level == LVL.local &&
+                    scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
+                {
+                    error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
+                    scw &= ~scwx;
+                }
                 if (level & (LVL.parameter | LVL.prototype) &&
                     scw & ~SCW.xregister)
                 {
@@ -2568,6 +2656,7 @@ final class CParser(AST) : Parser!AST
             }
 
             case TKW.xtag:
+            case TKW.x_Atomic:  // no atomics for you
                 break;          // t is already set
 
             default:
@@ -2806,7 +2895,10 @@ final class CParser(AST) : Parser!AST
 
                         auto parameterList = cparseParameterList();
                         const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage;
-                        AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, 0);
+                        StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
+                        if (specifier._pure)
+                            stc |= STC.pure_;
+                        AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
     //                  tf = tf.addSTC(storageClass);  // TODO
                         insertTx(ts, tf, t);  // ts -> ... -> tf -> t
 
@@ -2959,8 +3051,7 @@ final class CParser(AST) : Parser!AST
         if (token.value == TOK.rightParenthesis)        // func()
         {
             nextToken();
-            importBuiltins = true;                              // will need __va_list_tag
-            return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc);
+            return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc);
         }
 
         /* Create function prototype scope
@@ -3084,9 +3175,15 @@ final class CParser(AST) : Parser!AST
      *    extended-decl-modifier extended-decl-modifier-seq
      *
      * extended-decl-modifier:
+     *    align(number)
+     *    deprecated(depMsg)
      *    dllimport
      *    dllexport
+     *    naked
+     *    noinline
      *    noreturn
+     *    nothrow
+     *    thread
      * Params:
      *  specifier = filled in with the attribute(s)
      */
@@ -3096,8 +3193,6 @@ final class CParser(AST) : Parser!AST
         /* Check for dllexport, dllimport
          * Ignore the rest
          */
-        bool dllimport;  // TODO implement
-        bool dllexport;  // TODO implement
         nextToken();     // move past __declspec
         check(TOK.leftParenthesis);
         while (1)
@@ -3113,12 +3208,22 @@ final class CParser(AST) : Parser!AST
             {
                 if (token.ident == Id.dllimport)
                 {
-                    dllimport = true;
+                    specifier.dllimport = true;
                     nextToken();
                 }
                 else if (token.ident == Id.dllexport)
                 {
-                    dllexport = true;
+                    specifier.dllexport = true;
+                    nextToken();
+                }
+                else if (token.ident == Id.naked)
+                {
+                    specifier.naked = true;
+                    nextToken();
+                }
+                else if (token.ident == Id.noinline)
+                {
+                    specifier.scw |= SCW.xnoinline;
                     nextToken();
                 }
                 else if (token.ident == Id.noreturn)
@@ -3126,6 +3231,49 @@ final class CParser(AST) : Parser!AST
                     specifier.noreturn = true;
                     nextToken();
                 }
+                else if (token.ident == Id._nothrow)
+                {
+                    specifier._nothrow = true;
+                    nextToken();
+                }
+                else if (token.ident == Id.thread)
+                {
+                    specifier.scw |= SCW.x_Thread_local;
+                    nextToken();
+                }
+                else if (token.ident == Id._align)
+                {
+                    // Microsoft spec is very imprecise as to how this actually works
+                    nextToken();
+                    check(TOK.leftParenthesis);
+                    if (token.value == TOK.int32Literal)
+                    {
+                        const n = token.unsvalue;
+                        if (n < 1 || n & (n - 1) || 8192 < n)
+                            error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
+                        specifier.packalign.set(cast(uint)n);
+                        specifier.packalign.setPack(true);
+                        nextToken();
+                    }
+                    else
+                    {
+                        error("alignment value expected, not `%s`", token.toChars());
+                        nextToken();
+                    }
+
+                    check(TOK.rightParenthesis);
+                }
+                else if (token.ident == Id._deprecated)
+                {
+                    specifier._deprecated = true;
+                    nextToken();
+                    if (token.value == TOK.leftParenthesis)  // optional deprecation message
+                    {
+                        nextToken();
+                        specifier.depMsg = cparseExpression();
+                        check(TOK.rightParenthesis);
+                    }
+                }
                 else
                 {
                     nextToken();
@@ -3133,6 +3281,8 @@ final class CParser(AST) : Parser!AST
                         cparseParens();
                 }
             }
+            else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
+                nextToken();
             else
             {
                 error("extended-decl-modifier expected");
@@ -3142,7 +3292,8 @@ final class CParser(AST) : Parser!AST
     }
 
     /*************************
-     * Simple asm parser
+     * Parser for asm label. It appears after the declarator, and has apparently
+     * nothing to do with inline assembler.
      * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
      * simple-asm-expr:
      *   asm ( asm-string-literal )
@@ -3150,17 +3301,107 @@ final class CParser(AST) : Parser!AST
      * asm-string-literal:
      *   string-literal
      */
-    private AST.StringExp cparseSimpleAsmExpr()
+    private AST.StringExp cparseGnuAsmLabel()
     {
         nextToken();     // move past asm
         check(TOK.leftParenthesis);
         if (token.value != TOK.string_)
-            error("string literal expected");
+            error("string literal expected for Asm Label, not `%s`", token.toChars());
         auto label = cparsePrimaryExp();
         check(TOK.rightParenthesis);
         return cast(AST.StringExp) label;
     }
 
+    /********************
+     * Parse C inline assembler statement in Gnu format.
+     * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
+     *   asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
+     * Current token is on the `asm`.
+     * Returns:
+     *   inline assembler expression as a Statement
+     */
+    private AST.Statement cparseGnuAsm()
+    {
+        // Defer parsing of AsmStatements until semantic processing.
+        const loc = token.loc;
+
+        nextToken();
+
+        // Consume all asm-qualifiers. As a future optimization, we could record
+        // the `inline` and `volatile` storage classes against the statement.
+        while (token.value == TOK.goto_ ||
+               token.value == TOK.inline ||
+               token.value == TOK.volatile)
+            nextToken();
+
+        check(TOK.leftParenthesis);
+        if (token.value != TOK.string_)
+            error("string literal expected for Assembler Template, not `%s`", token.toChars());
+        Token* toklist = null;
+        Token** ptoklist = &toklist;
+        //Identifier label = null;
+        auto statements = new AST.Statements();
+
+        int parens;
+        while (1)
+        {
+            switch (token.value)
+            {
+                case TOK.leftParenthesis:
+                    ++parens;
+                    goto default;
+
+                case TOK.rightParenthesis:
+                    --parens;
+                    if (parens >= 0)
+                        goto default;
+                    break;
+
+                case TOK.semicolon:
+                    error("matching `)` expected, not `;`");
+                    break;
+
+                case TOK.endOfFile:
+                    /* ( */
+                    error("matching `)` expected, not end of file");
+                    break;
+
+                case TOK.colonColon:  // treat as two separate : tokens for iasmgcc
+                    *ptoklist = allocateToken();
+                    memcpy(*ptoklist, &token, Token.sizeof);
+                    (*ptoklist).value = TOK.colon;
+                    ptoklist = &(*ptoklist).next;
+
+                    *ptoklist = allocateToken();
+                    memcpy(*ptoklist, &token, Token.sizeof);
+                    (*ptoklist).value = TOK.colon;
+                    ptoklist = &(*ptoklist).next;
+
+                    *ptoklist = null;
+                    nextToken();
+                    continue;
+
+                default:
+                    *ptoklist = allocateToken();
+                    memcpy(*ptoklist, &token, Token.sizeof);
+                    ptoklist = &(*ptoklist).next;
+                    *ptoklist = null;
+                    nextToken();
+                    continue;
+            }
+            if (toklist)
+            {
+                // Create AsmStatement from list of tokens we've saved
+                AST.Statement s = new AST.AsmStatement(token.loc, toklist);
+                statements.push(s);
+            }
+            break;
+        }
+        nextToken();
+        auto s = new AST.CompoundAsmStatement(loc, statements, 0);
+        return s;
+    }
+
     /*************************
      * __attribute__ parser
      * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
@@ -3222,25 +3463,75 @@ final class CParser(AST) : Parser!AST
      */
     private void cparseGnuAttribute(ref Specifier specifier)
     {
-        /* Check for dllimport, dllexport, vector_size(bytes)
+        /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
          * Ignore the rest
          */
-        bool dllimport;  // TODO implement
-        bool dllexport;  // TODO implement
-
         if (!isGnuAttributeName())
             return;
 
         if (token.value == TOK.identifier)
         {
-            if (token.ident == Id.dllimport)
+            if (token.ident == Id.aligned)
             {
-                dllimport = true;
+                nextToken();
+                if (token.value == TOK.leftParenthesis)
+                {
+                    nextToken();
+                    if (token.value == TOK.int32Literal)
+                    {
+                        const n = token.unsvalue;
+                        if (n < 1 || n & (n - 1) || ushort.max < n)
+                            error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
+                        specifier.packalign.set(cast(uint)n);
+                        specifier.packalign.setPack(true);
+                        nextToken();
+                    }
+                    else
+                    {
+                        error("alignment value expected, not `%s`", token.toChars());
+                        nextToken();
+                    }
+
+                    check(TOK.rightParenthesis);
+                }
+                /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
+                 * type on the target machine. It's the opposite of __attribute__((packed))
+                 */
+            }
+            else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
+            {
+                specifier.scw |= SCW.xinline;
+                nextToken();
+            }
+            else if (token.ident == Id._deprecated)
+            {
+                specifier._deprecated = true;
+                nextToken();
+                if (token.value == TOK.leftParenthesis)  // optional deprecation message
+                {
+                    nextToken();
+                    specifier.depMsg = cparseExpression();
+                    check(TOK.rightParenthesis);
+                }
+            }
+            else if (token.ident == Id.dllimport)
+            {
+                specifier.dllimport = true;
                 nextToken();
             }
             else if (token.ident == Id.dllexport)
             {
-                dllexport = true;
+                specifier.dllexport = true;
+                nextToken();
+            }
+            else if (token.ident == Id.naked)
+            {
+                specifier.naked = true;
+                nextToken();
+            }
+            else if (token.ident == Id.noinline)
+            {
+                specifier.scw |= SCW.xnoinline;
                 nextToken();
             }
             else if (token.ident == Id.noreturn)
@@ -3248,6 +3539,16 @@ final class CParser(AST) : Parser!AST
                 specifier.noreturn = true;
                 nextToken();
             }
+            else if (token.ident == Id._nothrow)
+            {
+                specifier._nothrow = true;
+                nextToken();
+            }
+            else if (token.ident == Id._pure)
+            {
+                specifier._pure = true;
+                nextToken();
+            }
             else if (token.ident == Id.vector_size)
             {
                 nextToken();
@@ -3408,7 +3709,8 @@ final class CParser(AST) : Parser!AST
          * https://en.cppreference.com/w/cpp/language/enum
          *   enum Identifier : Type
          */
-        AST.Type base = AST.Type.tint32;  // C11 6.7.2.2-4 implementation defined default base type
+        //AST.Type base = AST.Type.tint32;  // C11 6.7.2.2-4 implementation defined default base type
+        AST.Type base = null;               // C23 says base type is determined by enum member values
         if (token.value == TOK.colon)
         {
             nextToken();
@@ -3488,7 +3790,7 @@ final class CParser(AST) : Parser!AST
          * redeclaration, or reference to existing declaration.
          * Defer to the semantic() pass with a TypeTag.
          */
-        return new AST.TypeTag(loc, TOK.enum_, tag, base, members);
+        return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
     }
 
     /*************************************
@@ -3510,11 +3812,12 @@ final class CParser(AST) : Parser!AST
      * Params:
      *  loc = location of `struct` or `union`
      *  structOrUnion = TOK.struct_ or TOK.union_
+     *  packalign = alignment to use for struct members
      *  symbols = symbols to add struct-or-union declaration to
      * Returns:
      *  type of the struct
      */
-    private AST.Type cparseStruct(Loc loc, TOK structOrUnion, ref AST.Dsymbols* symbols)
+    private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
     {
         Identifier tag;
 
@@ -3553,7 +3856,7 @@ final class CParser(AST) : Parser!AST
          * redeclaration, or reference to existing declaration.
          * Defer to the semantic() pass with a TypeTag.
          */
-        return new AST.TypeTag(loc, structOrUnion, tag, null, members);
+        return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
     }
 
     /*************************************
@@ -3996,6 +4299,13 @@ final class CParser(AST) : Parser!AST
                 case TOK.union_:
                 case TOK.enum_:
                     t = peek(t);
+                    if (t.value == TOK.__attribute__ ||
+                        t.value == TOK.__declspec)
+                    {
+                        t = peek(t);
+                        if (!skipParens(t, &t))
+                            return false;
+                    }
                     if (t.value == TOK.identifier)
                     {
                         t = peek(t);
@@ -4019,6 +4329,7 @@ final class CParser(AST) : Parser!AST
                 case TOK.typedef_:
                 case TOK.extern_:
                 case TOK.static_:
+                case TOK.__thread:
                 case TOK._Thread_local:
                 case TOK.auto_:
                 case TOK.register:
@@ -4620,6 +4931,8 @@ final class CParser(AST) : Parser!AST
         // C11 6.7.4 Function specifiers
         xinline    = 0x40,
         x_Noreturn = 0x80,
+
+        xnoinline  = 0x100,
     }
 
     /// C11 6.7.3 Type qualifiers
@@ -4639,6 +4952,14 @@ final class CParser(AST) : Parser!AST
     struct Specifier
     {
         bool noreturn;  /// noreturn attribute
+        bool naked;     /// naked attribute
+        bool _nothrow;  /// nothrow attribute
+        bool _pure;     /// pure attribute
+        bool dllimport; /// dllimport attribute
+        bool dllexport; /// dllexport attribute
+        bool _deprecated;       /// deprecated attribute
+        AST.Expression depMsg;  /// deprecated message
+
         SCW scw;        /// storage-class specifiers
         MOD mod;        /// type qualifiers
         AST.Expressions*  alignExps;  /// alignment
@@ -4662,6 +4983,8 @@ final class CParser(AST) : Parser!AST
             {
                 if (specifier.scw & SCW.xextern)
                     stc = AST.STC.extern_;
+                else if (specifier.scw & SCW.xstatic)
+                    stc = AST.STC.static_;
             }
             else if (level == LVL.local)
             {
@@ -4713,9 +5036,41 @@ final class CParser(AST) : Parser!AST
                     stc = AST.STC.gshared;
             }
         }
+        if (specifier._deprecated && !specifier.depMsg)
+            stc |= AST.STC.deprecated_;
         return stc;
     }
 
+    /***********************
+     * Add attributes from Specifier to function
+     * Params:
+     *  fd = function to apply them to
+     *  specifier = specifiers
+     */
+    void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
+    {
+        fd.isNaked = specifier.naked;
+        fd.dllImport = specifier.dllimport;
+        fd.dllExport = specifier.dllexport;
+
+        if (specifier.scw & SCW.xnoinline)
+            fd.inlining = PINLINE.never;
+        else if (specifier.scw & SCW.xinline)
+            fd.inlining = PINLINE.always;
+    }
+
+    /***********************
+     * Add attributes from Specifier to variable
+     * Params:
+     *  vd = function to apply them to
+     *  specifier = specifiers
+     */
+    void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
+    {
+        vd.dllImport = specifier.dllimport;
+        vd.dllExport = specifier.dllexport;
+    }
+
     /***********************
      * Return suitable signed integer type for the given size
      * Params:
@@ -4826,7 +5181,7 @@ final class CParser(AST) : Parser!AST
         auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
         auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
         efn.type = tfn.immutableOf();
-        efn.committed = 1;
+        efn.committed = true;
         auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
         auto e = new AST.DeclarationExp(loc, sfn);
         return new AST.ExpStatement(loc, e);
@@ -4879,6 +5234,17 @@ final class CParser(AST) : Parser!AST
     private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
     {
         //printf("applySpecifier() %s\n", s.toChars());
+        if (specifier._deprecated)
+        {
+            if (specifier.depMsg)
+            {
+                // Wrap declaration in a DeprecatedDeclaration
+                auto decls = new AST.Dsymbols(1);
+                (*decls)[0] = s;
+                s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
+            }
+        }
+
         if (specifier.alignExps)
         {
             //printf("  applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
index 32b3851895389ba4dd9b095ff7ca641b0f51c516..40092c3f584e5602c916cce0f1806fc68f066a4b 100644 (file)
@@ -211,7 +211,7 @@ private final class CppMangleVisitor : Visitor
      */
     void mangleReturnType(TypeFunction preSemantic)
     {
-        auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
+        auto tf = this.context.res.asFuncDecl().type.isTypeFunction();
         Type rt = preSemantic.nextOf();
         // https://issues.dlang.org/show_bug.cgi?id=22739
         // auto return type means that rt is null.
@@ -347,14 +347,14 @@ private final class CppMangleVisitor : Visitor
      *
      * Params:
      *   off  = Offset to insert at
-     *   fd   = Type of the function to mangle the return type of
+     *   tf   = Type of the function to mangle the return type of
      */
     void writeRemainingTags(size_t off, TypeFunction tf)
     {
-        scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written);
-        tf.next.accept(remainingVisitor);
+        Array!StringExp toWrite;
+        leftOver(tf, &this.abiTags.written, &toWrite);
         OutBuffer b2;
-        foreach (se; remainingVisitor.toWrite)
+        foreach (se; toWrite)
         {
             auto tag = se.peekString();
             // We can only insert a slice, and each insert is a memmove,
@@ -496,9 +496,9 @@ private final class CppMangleVisitor : Visitor
                 mangle_function(d.isFuncDeclaration());
                 buf.writestring("EE");
             }
-            else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration())
+            else if (e && e.isVarExp() && e.isVarExp().var.isVarDeclaration())
             {
-                VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration();
+                VarDeclaration vd = e.isVarExp().var.isVarDeclaration();
                 buf.writeByte('L');
                 mangle_variable(vd, true);
                 buf.writeByte('E');
@@ -757,9 +757,9 @@ private final class CppMangleVisitor : Visitor
     bool isIdent_char(Identifier ident, RootObject o)
     {
         Type t = isType(o);
-        if (!t || t.ty != Tstruct)
+        if (!t || !t.isTypeStruct())
             return false;
-        Dsymbol s = (cast(TypeStruct)t).toDsymbol(null);
+        Dsymbol s = t.toDsymbol(null);
         if (s.ident != ident)
             return false;
         Dsymbol p = s.toParent();
@@ -1059,7 +1059,7 @@ private final class CppMangleVisitor : Visitor
          *            ::= <data name>
          *            ::= <special-name>
          */
-        TypeFunction tf = cast(TypeFunction)d.type;
+        TypeFunction tf = d.type.isTypeFunction();
 
         if (TemplateDeclaration ftd = getFuncTemplateDecl(d))
         {
@@ -1173,7 +1173,7 @@ private final class CppMangleVisitor : Visitor
             this.context.ti = ti;
             this.context.fd = d;
             this.context.res = d;
-            TypeFunction preSemantic = cast(TypeFunction)d.originalType;
+            TypeFunction preSemantic = d.originalType.isTypeFunction();
             auto nspace = ti.toParent();
             if (nspace && nspace.isNspace())
                 this.writeChained(ti.toParent(), () => source_name(ti, true));
@@ -1347,7 +1347,7 @@ private final class CppMangleVisitor : Visitor
             auto prev = this.context.push({
                     TypeFunction tf;
                     if (isDsymbol(this.context.res))
-                        tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
+                        tf = this.context.res.asFuncDecl().type.isTypeFunction();
                     else
                         tf = this.context.res.asType().isTypeFunction();
                     assert(tf);
@@ -1391,9 +1391,9 @@ private final class CppMangleVisitor : Visitor
      */
     void headOfType(Type t)
     {
-        if (t.ty == Tclass)
+        if (auto tc = t.isTypeClass())
         {
-            mangleTypeClass(cast(TypeClass)t, true);
+            mangleTypeClass(tc, true);
         }
         else
         {
@@ -1960,7 +1960,7 @@ extern(C++):
      */
     override void visit(TypeIdentifier t)
     {
-        auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
+        auto decl = this.context.ti.tempdecl.isTemplateDeclaration();
         assert(decl.parameters !is null);
         auto idx = templateParamIndex(t.ident, decl.parameters);
         // If not found, default to the post-semantic type
@@ -2019,7 +2019,7 @@ extern(C++):
             {
                 // If the resolved AST has more args than the parse one,
                 // we have default arguments
-                auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters;
+                auto oparams = analyzed_ti.tempdecl.isTemplateDeclaration().origParameters;
                 foreach (idx, arg; (*oparams)[t.tiargs.length .. $])
                 {
                     this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.length];
@@ -2044,7 +2044,7 @@ extern(C++):
         assert(t.tiargs !is null);
 
         bool needsTa;
-        auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
+        auto decl = this.context.ti.tempdecl.isTemplateDeclaration();
         // Attempt to substitute the template itself
         auto idx = templateParamIndex(t.name, decl.parameters);
         if (idx < decl.parameters.length)
@@ -2125,12 +2125,13 @@ private void visitObject(V : Visitor)(RootObject o, V this_)
 /// Helper function to safely get a type out of a `RootObject`
 private Type asType(RootObject o)
 {
-    Type ta = isType(o);
+    if (Type ta = isType(o))
+        return ta;
+
     // When called with context.res as argument, it can be `FuncDeclaration`
-    if (!ta && o.asFuncDecl())
-        ta = (cast(FuncDeclaration)o).type;
-    assert(ta !is null, o.toString());
-    return ta;
+    if (auto fd = o.asFuncDecl())
+        return fd.type;
+    assert(0);
 }
 
 /// Helper function to safely get a `FuncDeclaration` out of a `RootObject`
@@ -2183,12 +2184,12 @@ private extern(C++) final class ComponentVisitor : Visitor
 
         case DYNCAST.type:
             auto t = cast(Type)base;
-            if (t.ty == Tpointer)
-                this.tpointer = cast(TypePointer)t;
-            else if (t.ty == Treference)
-                this.tref = cast(TypeReference)t;
-            else if (t.ty == Tident)
-                this.tident = cast(TypeIdentifier)t;
+            if (auto tp = t.isTypePointer())
+                this.tpointer = tp;
+            else if (auto tr = t.isTypeReference())
+                this.tref = tr;
+            else if (auto ti = t.isTypeIdentifier())
+                this.tident = ti;
             else
                 goto default;
             break;
@@ -2531,58 +2532,70 @@ unittest
     assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match);
 }
 
-/**
+/***
  * Visits the return type of a function and writes leftover ABI tags
+ * Params:
+ *   tf = Type of the function to mangle the return type of
+ *   previous = already written ones
+ *   toWrite = where to put StringExp's to be written
  */
-extern(C++) private final class LeftoverVisitor : Visitor
+private
+void leftOver(TypeFunction tf, const(Array!StringExp)* previous, Array!StringExp* toWrite)
 {
-    /// List of tags to write
-    private Array!StringExp toWrite;
-    /// List of tags to ignore
-    private const(Array!StringExp)* ignore;
-
-    ///
-    public this(const(Array!StringExp)* previous)
+    extern(C++) final class LeftoverVisitor : Visitor
     {
-        this.ignore = previous;
-    }
+        /// List of tags to write
+        private Array!StringExp* toWrite;
+        /// List of tags to ignore
+        private const(Array!StringExp)* ignore;
 
-    /// Reintroduce base class overloads
-    public alias visit = Visitor.visit;
+        ///
+        public this(const(Array!StringExp)* previous, Array!StringExp* toWrite)
+        {
+            this.ignore = previous;
+            this.toWrite = toWrite;
+        }
 
-    /// Least specialized overload of each direct child of `RootObject`
-    public override void visit(Dsymbol o)
-    {
-        auto ale = ABITagContainer.forSymbol(o);
-        if (!ale) return;
+        /// Reintroduce base class overloads
+        public alias visit = Visitor.visit;
 
-        bool match;
-        foreach (elem; *ale.elements)
+        /// Least specialized overload of each direct child of `RootObject`
+        public override void visit(Dsymbol o)
         {
-            auto se = elem.toStringExp();
-            closestIndex((*this.ignore)[], se, match);
-            if (match) continue;
-            auto idx = closestIndex(this.toWrite[], se, match);
-            if (!match)
-                this.toWrite.insert(idx, se);
+            auto ale = ABITagContainer.forSymbol(o);
+            if (!ale) return;
+
+            bool match;
+            foreach (elem; *ale.elements)
+            {
+                auto se = elem.toStringExp();
+                closestIndex((*this.ignore)[], se, match);
+                if (match) continue;
+                auto idx = closestIndex((*this.toWrite)[], se, match);
+                if (!match)
+                    (*this.toWrite).insert(idx, se);
+            }
         }
-    }
 
-    /// Ditto
-    public override void visit(Type o)
-    {
-        if (auto sym = o.toDsymbol(null))
-            sym.accept(this);
-    }
+        /// Ditto
+        public override void visit(Type o)
+        {
+            if (auto sym = o.toDsymbol(null))
+                sym.accept(this);
+        }
 
-    /// Composite type
-    public override void visit(TypePointer o)
-    {
-        o.next.accept(this);
-    }
+        /// Composite type
+        public override void visit(TypePointer o)
+        {
+            o.next.accept(this);
+        }
 
-    public override void visit(TypeReference o)
-    {
-        o.next.accept(this);
+        public override void visit(TypeReference o)
+        {
+            o.next.accept(this);
+        }
     }
+
+    scope remainingVisitor = new LeftoverVisitor(previous, toWrite);
+    tf.next.accept(remainingVisitor);
 }
index 8109e12d43d34daab0506e6422329a73f1a0e674..289ebeb81ca9ca10a9b8844d987c071791633096 100644 (file)
@@ -47,7 +47,7 @@ extern (C++) final class ClassReferenceExp : Expression
 
     extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type)
     {
-        super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp));
+        super(loc, EXP.classReference);
         assert(lit && lit.sd && lit.sd.isClassDeclaration());
         this.value = lit;
         this.type = type;
@@ -132,7 +132,7 @@ extern (C++) final class ThrownExceptionExp : Expression
 
     extern (D) this(const ref Loc loc, ClassReferenceExp victim)
     {
-        super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
+        super(loc, EXP.thrownException);
         this.thrown = victim;
         this.type = victim.type;
     }
@@ -170,7 +170,7 @@ extern (C++) final class CTFEExp : Expression
 {
     extern (D) this(EXP tok)
     {
-        super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp));
+        super(Loc.initial, tok);
         type = Type.tvoid;
     }
 
@@ -369,7 +369,6 @@ UnionExp copyLiteral(Expression e)
     case EXP.dotVariable:
     case EXP.int64:
     case EXP.float64:
-    case EXP.char_:
     case EXP.complex80:
     case EXP.void_:
     case EXP.vector:
@@ -1468,7 +1467,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         memset(cast(char*)s + len * sz, 0, sz);
         emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
         StringExp es = ue.exp().isStringExp();
-        es.committed = 0;
+        es.committed = false;
         es.type = type;
         return ue;
     }
@@ -1499,7 +1498,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
         StringExp es = ue.exp().isStringExp();
         es.sz = sz;
-        es.committed = 0; //es1.committed;
+        es.committed = false; //es1.committed;
         es.type = type;
         return ue;
     }
@@ -1837,7 +1836,6 @@ bool isCtfeValueValid(Expression newval)
     {
         case EXP.int64:
         case EXP.float64:
-        case EXP.char_:
         case EXP.complex80:
             return tb.isscalar();
 
index 2830b25d651210af1cb5cd09eb3f0fb3ed8d21a9..8ffbef3c9669cb695fdb1217e1994d2f82ab0f48 100644 (file)
@@ -1845,7 +1845,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         if (!e.committed)
         {
             se = e.copy().isStringExp();
-            se.committed = 1;
+            se.committed = true;
             copied = 1;
         }
 
@@ -1887,7 +1887,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             assert(szx <= 255);
             se.sz = cast(ubyte)szx;
             se.len = cast(size_t)tb.isTypeSArray().dim.toInteger();
-            se.committed = 1;
+            se.committed = true;
             se.type = t;
 
             /* If larger than source, pad with zeros.
index 7cd8df19bdcb0fab5dcd39c06a6efe611f4fa077..0e5df5eb5502471fbf406a599d74cc3ee31118f3 100644 (file)
@@ -738,7 +738,7 @@ extern (C++) final class AliasDeclaration : Declaration
     extern (D) this(const ref Loc loc, Identifier ident, Type type)
     {
         super(loc, ident);
-        //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type);
+        //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type);
         //printf("type = '%s'\n", type.toChars());
         this.type = type;
         assert(type);
@@ -747,7 +747,7 @@ extern (C++) final class AliasDeclaration : Declaration
     extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s)
     {
         super(loc, ident);
-        //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s);
+        //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s);
         assert(s != this);
         this.aliassym = s;
         assert(s);
@@ -1149,6 +1149,8 @@ extern (C++) class VarDeclaration : Declaration
 
         bool isArgDtorVar;      /// temporary created to handle scope destruction of a function argument
         bool isCmacro;          /// it is a C macro turned into a C declaration
+        bool dllImport;         /// __declspec(dllimport)
+        bool dllExport;         /// __declspec(dllexport)
         version (MARS)
         {
             bool inClosure;         /// is inserted into a GC allocated closure
@@ -1314,7 +1316,7 @@ extern (C++) class VarDeclaration : Declaration
 
     override final bool isExport() const
     {
-        return visibility.kind == Visibility.Kind.export_;
+        return visibility.kind == Visibility.Kind.export_ || dllExport;
     }
 
     override final bool isImportedSymbol() const
@@ -1325,6 +1327,7 @@ extern (C++) class VarDeclaration : Declaration
          *   export extern int sym3 = 0; // error, extern cannot have initializer
          */
         bool result =
+            dllImport ||
             visibility.kind == Visibility.Kind.export_ &&
             storage_class & STC.extern_ &&
             (storage_class & STC.static_ || parent.isModule());
@@ -1931,8 +1934,12 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
         {
             // If the bit-field spans more units of alignment than its type,
             // start a new field at the next alignment boundary.
-            if (fieldState.bitOffset == fieldState.fieldSize * 8)
+            if (fieldState.bitOffset == fieldState.fieldSize * 8 &&
+                fieldState.bitOffset + fieldWidth > memalignsize * 8)
+            {
+                if (log) printf("more units of alignment than its type\n");
                 startNewField();        // the bit field is full
+            }
             else
             {
                 // if alignment boundary is crossed
@@ -1941,7 +1948,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
                 //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize);
                 if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8))
                 {
-                    //printf("alignment is crossed\n");
+                    if (log) printf("alignment is crossed\n");
                     startNewField();
                 }
             }
@@ -1981,6 +1988,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
             fieldState.bitOffset = pastField;
         }
 
+        //printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize);
         //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
         //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize);
     }
index d75f64f0766549fef6de95bf527201ece9700283..7a69382e6ba597228fde0b5056b301a3a6da1932 100644 (file)
@@ -623,6 +623,9 @@ public:
     FuncDeclarations *inlinedNestedCallees;
 
     AttributeViolation* safetyViolation;
+    AttributeViolation* nogcViolation;
+    AttributeViolation* pureViolation;
+    AttributeViolation* nothrowViolation;
 
     // Formerly FUNCFLAGS
     uint32_t flags;
@@ -672,6 +675,10 @@ public:
     bool isCrtCtor(bool v);
     bool isCrtDtor() const;
     bool isCrtDtor(bool v);
+    bool dllImport() const;
+    bool dllImport(bool v);
+    bool dllExport() const;
+    bool dllExport(bool v);
 
     // Data for a function declaration that is needed for the Objective-C
     // integration.
index e6ef704be8688e7d65e015af73431bfc7d92c42c..4ef6a39207339ca275e7b33ef3e55dc8497edf4e 100644 (file)
@@ -642,7 +642,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
             e = CTFEExp.cantexp;
             break;
         }
-        e = interpret(pue, fd.fbody, &istatex);
+        e = interpretStatement(pue, fd.fbody, &istatex);
         if (CTFEExp.isCantExp(e))
         {
             debug (LOG)
@@ -738,21 +738,30 @@ void incUsageCtfe(InterState* istate, const ref Loc loc)
     }
 }
 
-private extern (C++) final class Interpreter : Visitor
+/***********************************
+ * Interpret the statement.
+ * Params:
+ *    s = Statement to interpret
+ *    istate = context
+ * Returns:
+ *      NULL    continue to next statement
+ *      EXP.cantExpression      cannot interpret statement at compile time
+ *      !NULL   expression from return statement, or thrown exception
+ */
+
+Expression interpretStatement(Statement s, InterState* istate)
 {
-    alias visit = Visitor.visit;
-public:
-    InterState* istate;
-    CTFEGoal goal;
-    Expression result;
-    UnionExp* pue;              // storage for `result`
+    UnionExp ue = void;
+    auto result = interpretStatement(&ue, s, istate);
+    if (result == ue.exp())
+        result = ue.copy();
+    return result;
+}
 
-    extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope
-    {
-        this.pue = pue;
-        this.istate = istate;
-        this.goal = goal;
-    }
+///
+Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate)
+{
+    Expression result;
 
     // If e is EXP.throw_exception or EXP.cantExpression,
     // set it to 'result' and returns true.
@@ -767,26 +776,13 @@ public:
         return false;
     }
 
-    static Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original)
-    {
-        if (exps is original)
-        {
-            if (!original)
-                exps = new Expressions();
-            else
-                exps = original.copy();
-            ++ctfeGlobals.numArrayAllocs;
-        }
-        return exps;
-    }
-
     /******************************** Statement ***************************/
 
-    override void visit(Statement s)
+    void visitDefaultCase(Statement s)
     {
         debug (LOG)
         {
-            printf("%s Statement::interpret()\n", s.loc.toChars());
+            printf("%s Statement::interpret() %s\n", s.loc.toChars(), s.toChars());
         }
         if (istate.start)
         {
@@ -799,7 +795,7 @@ public:
         result = CTFEExp.cantexp;
     }
 
-    override void visit(ExpStatement s)
+    void visitExp(ExpStatement s)
     {
         debug (LOG)
         {
@@ -819,7 +815,12 @@ public:
             return;
     }
 
-    override void visit(CompoundStatement s)
+    void visitDtorExp(DtorExpStatement s)
+    {
+        visitExp(s);
+    }
+
+    void visitCompound(CompoundStatement s)
     {
         debug (LOG)
         {
@@ -832,7 +833,7 @@ public:
         foreach (i; 0 .. dim)
         {
             Statement sx = (*s.statements)[i];
-            result = interpret(pue, sx, istate);
+            result = interpretStatement(pue, sx, istate);
             if (result)
                 break;
         }
@@ -842,7 +843,12 @@ public:
         }
     }
 
-    override void visit(UnrolledLoopStatement s)
+    void visitCompoundAsm(CompoundAsmStatement s)
+    {
+        visitCompound(s);
+    }
+
+    void visitUnrolledLoop(UnrolledLoopStatement s)
     {
         debug (LOG)
         {
@@ -855,7 +861,7 @@ public:
         foreach (i; 0 .. dim)
         {
             Statement sx = (*s.statements)[i];
-            Expression e = interpret(pue, sx, istate);
+            Expression e = interpretStatement(pue, sx, istate);
             if (!e) // succeeds to interpret, or goto target was not found
                 continue;
             if (exceptionOrCant(e))
@@ -888,7 +894,7 @@ public:
         }
     }
 
-    override void visit(IfStatement s)
+    void visitIf(IfStatement s)
     {
         debug (LOG)
         {
@@ -900,9 +906,9 @@ public:
         if (istate.start)
         {
             Expression e = null;
-            e = interpret(s.ifbody, istate);
+            e = interpretStatement(s.ifbody, istate);
             if (!e && istate.start)
-                e = interpret(s.elsebody, istate);
+                e = interpretStatement(s.elsebody, istate);
             result = e;
             return;
         }
@@ -914,9 +920,9 @@ public:
             return;
 
         if (isTrueBool(e))
-            result = interpret(pue, s.ifbody, istate);
+            result = interpretStatement(pue, s.ifbody, istate);
         else if (e.toBool().hasValue(false))
-            result = interpret(pue, s.elsebody, istate);
+            result = interpretStatement(pue, s.elsebody, istate);
         else
         {
             // no error, or assert(0)?
@@ -924,7 +930,7 @@ public:
         }
     }
 
-    override void visit(ScopeStatement s)
+    void visitScope(ScopeStatement s)
     {
         debug (LOG)
         {
@@ -933,78 +939,10 @@ public:
         if (istate.start == s)
             istate.start = null;
 
-        result = interpret(pue, s.statement, istate);
+        result = interpretStatement(pue, s.statement, istate);
     }
 
-    /**
-     Given an expression e which is about to be returned from the current
-     function, generate an error if it contains pointers to local variables.
-
-     Only checks expressions passed by value (pointers to local variables
-     may already be stored in members of classes, arrays, or AAs which
-     were passed as mutable function parameters).
-     Returns:
-        true if it is safe to return, false if an error was generated.
-     */
-    static bool stopPointersEscaping(const ref Loc loc, Expression e)
-    {
-        if (!e.type.hasPointers())
-            return true;
-        if (isPointer(e.type))
-        {
-            Expression x = e;
-            if (auto eaddr = e.isAddrExp())
-                x = eaddr.e1;
-            VarDeclaration v;
-            while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null)
-            {
-                if (v.storage_class & STC.ref_)
-                {
-                    x = getValue(v);
-                    if (auto eaddr = e.isAddrExp())
-                        eaddr.e1 = x;
-                    continue;
-                }
-                if (ctfeGlobals.stack.isInCurrentFrame(v))
-                {
-                    error(loc, "returning a pointer to a local stack variable");
-                    return false;
-                }
-                else
-                    break;
-            }
-            // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not
-            // pointing to a local struct or static array.
-        }
-        if (auto se = e.isStructLiteralExp())
-        {
-            return stopPointersEscapingFromArray(loc, se.elements);
-        }
-        if (auto ale = e.isArrayLiteralExp())
-        {
-            return stopPointersEscapingFromArray(loc, ale.elements);
-        }
-        if (auto aae = e.isAssocArrayLiteralExp())
-        {
-            if (!stopPointersEscapingFromArray(loc, aae.keys))
-                return false;
-            return stopPointersEscapingFromArray(loc, aae.values);
-        }
-        return true;
-    }
-
-    // Check all elements of an array for escaping local variables. Return false if error
-    static bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems)
-    {
-        foreach (e; *elems)
-        {
-            if (e && !stopPointersEscaping(loc, e))
-                return false;
-        }
-        return true;
-    }
-
-    override void visit(ReturnStatement s)
+    void visitReturn(ReturnStatement s)
     {
         debug (LOG)
         {
@@ -1061,7 +999,7 @@ public:
         if (isRuntimeHook(s.exp, Id._d_arrayappendT) || isRuntimeHook(s.exp, Id._d_arrayappendTTrace))
         {
             auto rs = new ReturnStatement(s.loc, e);
-            rs.accept(this);
+            visitReturn(rs);
             return;
         }
 
@@ -1082,20 +1020,7 @@ public:
         result = e;
     }
 
-    static Statement findGotoTarget(InterState* istate, Identifier ident)
-    {
-        Statement target = null;
-        if (ident)
-        {
-            LabelDsymbol label = istate.fd.searchLabel(ident);
-            assert(label && label.statement);
-            LabelStatement ls = label.statement;
-            target = ls.gotoTarget ? ls.gotoTarget : ls.statement;
-        }
-        return target;
-    }
-
-    override void visit(BreakStatement s)
+    void visitBreak(BreakStatement s)
     {
         debug (LOG)
         {
@@ -1113,7 +1038,7 @@ public:
         result = CTFEExp.breakexp;
     }
 
-    override void visit(ContinueStatement s)
+    void visitContinue(ContinueStatement s)
     {
         debug (LOG)
         {
@@ -1131,7 +1056,7 @@ public:
         result = CTFEExp.continueexp;
     }
 
-    override void visit(WhileStatement s)
+    void visitWhile(WhileStatement s)
     {
         debug (LOG)
         {
@@ -1140,7 +1065,7 @@ public:
         assert(0); // rewritten to ForStatement
     }
 
-    override void visit(DoStatement s)
+    void visitDo(DoStatement s)
     {
         debug (LOG)
         {
@@ -1151,7 +1076,7 @@ public:
 
         while (1)
         {
-            Expression e = interpret(s._body, istate);
+            Expression e = interpretStatement(s._body, istate);
             if (!e && istate.start) // goto target was not found
                 return;
             assert(!istate.start);
@@ -1201,7 +1126,7 @@ public:
         assert(result is null);
     }
 
-    override void visit(ForStatement s)
+    void visitFor(ForStatement s)
     {
         debug (LOG)
         {
@@ -1211,7 +1136,7 @@ public:
             istate.start = null;
 
         UnionExp ueinit = void;
-        Expression ei = interpret(&ueinit, s._init, istate);
+        Expression ei = interpretStatement(&ueinit, s._init, istate);
         if (exceptionOrCant(ei))
             return;
         assert(!ei); // s.init never returns from function, or jumps out from it
@@ -1230,7 +1155,7 @@ public:
                 assert(isTrueBool(e));
             }
 
-            Expression e = interpret(pue, s._body, istate);
+            Expression e = interpretStatement(pue, s._body, istate);
             if (!e && istate.start) // goto target was not found
                 return;
             assert(!istate.start);
@@ -1273,17 +1198,17 @@ public:
         assert(result is null);
     }
 
-    override void visit(ForeachStatement s)
+    void visitForeach(ForeachStatement s)
     {
         assert(0); // rewritten to ForStatement
     }
 
-    override void visit(ForeachRangeStatement s)
+    void visitForeachRange(ForeachRangeStatement s)
     {
         assert(0); // rewritten to ForStatement
     }
 
-    override void visit(SwitchStatement s)
+    void visitSwitch(SwitchStatement s)
     {
         debug (LOG)
         {
@@ -1294,7 +1219,7 @@ public:
             istate.start = null;
         if (istate.start)
         {
-            Expression e = interpret(s._body, istate);
+            Expression e = interpretStatement(s._body, istate);
             if (istate.start) // goto target was not found
                 return;
             if (exceptionOrCant(e))
@@ -1344,7 +1269,7 @@ public:
         /* Jump to scase
          */
         istate.start = scase;
-        Expression e = interpret(pue, s._body, istate);
+        Expression e = interpretStatement(pue, s._body, istate);
         assert(!istate.start); // jump must not fail
         if (e && e.op == EXP.break_)
         {
@@ -1359,7 +1284,7 @@ public:
         result = e;
     }
 
-    override void visit(CaseStatement s)
+    void visitCase(CaseStatement s)
     {
         debug (LOG)
         {
@@ -1369,10 +1294,10 @@ public:
         if (istate.start == s)
             istate.start = null;
 
-        result = interpret(pue, s.statement, istate);
+        result = interpretStatement(pue, s.statement, istate);
     }
 
-    override void visit(DefaultStatement s)
+    void visitDefault(DefaultStatement s)
     {
         debug (LOG)
         {
@@ -1382,10 +1307,10 @@ public:
         if (istate.start == s)
             istate.start = null;
 
-        result = interpret(pue, s.statement, istate);
+        result = interpretStatement(pue, s.statement, istate);
     }
 
-    override void visit(GotoStatement s)
+    void visitGoto(GotoStatement s)
     {
         debug (LOG)
         {
@@ -1404,7 +1329,7 @@ public:
         result = CTFEExp.gotoexp;
     }
 
-    override void visit(GotoCaseStatement s)
+    void visitGotoCase(GotoCaseStatement s)
     {
         debug (LOG)
         {
@@ -1423,7 +1348,7 @@ public:
         result = CTFEExp.gotoexp;
     }
 
-    override void visit(GotoDefaultStatement s)
+    void visitGotoDefault(GotoDefaultStatement s)
     {
         debug (LOG)
         {
@@ -1442,7 +1367,7 @@ public:
         result = CTFEExp.gotoexp;
     }
 
-    override void visit(LabelStatement s)
+    void visitLabel(LabelStatement s)
     {
         debug (LOG)
         {
@@ -1451,10 +1376,10 @@ public:
         if (istate.start == s)
             istate.start = null;
 
-        result = interpret(pue, s.statement, istate);
+        result = interpretStatement(pue, s.statement, istate);
     }
 
-    override void visit(TryCatchStatement s)
+    void visitTryCatch(TryCatchStatement s)
     {
         debug (LOG)
         {
@@ -1465,18 +1390,18 @@ public:
         if (istate.start)
         {
             Expression e = null;
-            e = interpret(pue, s._body, istate);
+            e = interpretStatement(pue, s._body, istate);
             foreach (ca; *s.catches)
             {
                 if (e || !istate.start) // goto target was found
                     break;
-                e = interpret(pue, ca.handler, istate);
+                e = interpretStatement(pue, ca.handler, istate);
             }
             result = e;
             return;
         }
 
-        Expression e = interpret(s._body, istate);
+        Expression e = interpretStatement(s._body, istate);
 
         // An exception was thrown
         if (e && e.isThrownExceptionExp())
@@ -1497,7 +1422,7 @@ public:
                     ctfeGlobals.stack.push(ca.var);
                     setValue(ca.var, ex.thrown);
                 }
-                e = interpret(ca.handler, istate);
+                e = interpretStatement(ca.handler, istate);
                 if (CTFEExp.isGotoExp(e))
                 {
                     /* This is an optimization that relies on the locality of the jump target.
@@ -1509,7 +1434,7 @@ public:
                     InterState istatex = *istate;
                     istatex.start = istate.gotoTarget; // set starting statement
                     istatex.gotoTarget = null;
-                    Expression eh = interpret(ca.handler, &istatex);
+                    Expression eh = interpretStatement(ca.handler, &istatex);
                     if (!istatex.start)
                     {
                         istate.gotoTarget = null;
@@ -1522,39 +1447,7 @@ public:
         result = e;
     }
 
-    static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest)
-    {
-        debug (LOG)
-        {
-            printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars());
-        }
-        // Little sanity check to make sure it's really a Throwable
-        ClassReferenceExp boss = oldest.thrown;
-        const next = 5;                         // index of Throwable.next
-        assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
-        ClassReferenceExp collateral = newest.thrown;
-        if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException())
-        {
-            /* Find the index of the Error.bypassException field
-             */
-            auto bypass = next + 1;
-            if ((*collateral.value.elements)[bypass].type.ty == Tuns32)
-                bypass += 1;  // skip over _refcount field
-            assert((*collateral.value.elements)[bypass].type.ty == Tclass);
-
-            // The new exception bypass the existing chain
-            (*collateral.value.elements)[bypass] = boss;
-            return newest;
-        }
-        while ((*boss.value.elements)[next].op == EXP.classReference)
-        {
-            boss = (*boss.value.elements)[next].isClassReferenceExp();
-        }
-        (*boss.value.elements)[next] = collateral;
-        return oldest;
-    }
-
-    override void visit(TryFinallyStatement s)
+    void visitTryFinally(TryFinallyStatement s)
     {
         debug (LOG)
         {
@@ -1565,14 +1458,14 @@ public:
         if (istate.start)
         {
             Expression e = null;
-            e = interpret(pue, s._body, istate);
+            e = interpretStatement(pue, s._body, istate);
             // Jump into/out from finalbody is disabled in semantic analysis.
             // and jump inside will be handled by the ScopeStatement == finalbody.
             result = e;
             return;
         }
 
-        Expression ex = interpret(s._body, istate);
+        Expression ex = interpretStatement(s._body, istate);
         if (CTFEExp.isCantExp(ex))
         {
             result = ex;
@@ -1585,7 +1478,7 @@ public:
             InterState istatex = *istate;
             istatex.start = istate.gotoTarget; // set starting statement
             istatex.gotoTarget = null;
-            Expression bex = interpret(s._body, &istatex);
+            Expression bex = interpretStatement(s._body, &istatex);
             if (istatex.start)
             {
                 // The goto target is outside the current scope.
@@ -1601,7 +1494,7 @@ public:
             ex = bex;
         }
 
-        Expression ey = interpret(s.finalbody, istate);
+        Expression ey = interpretStatement(s.finalbody, istate);
         if (CTFEExp.isCantExp(ey))
         {
             result = ey;
@@ -1618,7 +1511,7 @@ public:
         result = ex;
     }
 
-    override void visit(ThrowStatement s)
+    void visitThrow(ThrowStatement s)
     {
         debug (LOG)
         {
@@ -1631,35 +1524,15 @@ public:
             istate.start = null;
         }
 
-        interpretThrow(s.exp, s.loc);
-    }
-
-    /// Interpret `throw <exp>` found at the specified location `loc`
-    private void interpretThrow(Expression exp, const ref Loc loc)
-    {
-        incUsageCtfe(istate, loc);
-
-        Expression e = interpretRegion(exp, istate);
-        if (exceptionOrCant(e))
-            return;
-
-        if (e.op == EXP.classReference)
-        {
-            result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp());
-        }
-        else
-        {
-            exp.error("to be thrown `%s` must be non-null", exp.toChars());
-            result = ErrorExp.get();
-        }
+        interpretThrow(result, s.exp, s.loc, istate);
     }
 
-    override void visit(ScopeGuardStatement s)
+    void visitScopeGuard(ScopeGuardStatement s)
     {
         assert(0);
     }
 
-    override void visit(WithStatement s)
+    void visitWith(WithStatement s)
     {
         debug (LOG)
         {
@@ -1669,14 +1542,14 @@ public:
             istate.start = null;
         if (istate.start)
         {
-            result = s._body ? interpret(s._body, istate) : null;
+            result = s._body ? interpretStatement(s._body, istate) : null;
             return;
         }
 
         // If it is with(Enum) {...}, just execute the body.
         if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type)
         {
-            result = interpret(pue, s._body, istate);
+            result = interpretStatement(pue, s._body, istate);
             return;
         }
 
@@ -1692,7 +1565,7 @@ public:
         }
         ctfeGlobals.stack.push(s.wthis);
         setValue(s.wthis, e);
-        e = interpret(s._body, istate);
+        e = interpretStatement(s._body, istate);
         if (CTFEExp.isGotoExp(e))
         {
             /* This is an optimization that relies on the locality of the jump target.
@@ -1704,7 +1577,7 @@ public:
             InterState istatex = *istate;
             istatex.start = istate.gotoTarget; // set starting statement
             istatex.gotoTarget = null;
-            Expression ex = interpret(s._body, &istatex);
+            Expression ex = interpretStatement(s._body, &istatex);
             if (!istatex.start)
             {
                 istate.gotoTarget = null;
@@ -1715,7 +1588,7 @@ public:
         result = e;
     }
 
-    override void visit(AsmStatement s)
+    void visitAsm(AsmStatement s)
     {
         debug (LOG)
         {
@@ -1731,7 +1604,17 @@ public:
         result = CTFEExp.cantexp;
     }
 
-    override void visit(ImportStatement s)
+    void visitInlineAsm(InlineAsmStatement s)
+    {
+        visitAsm(s);
+    }
+
+    void visitGccAsm(GccAsmStatement s)
+    {
+        visitAsm(s);
+    }
+
+    void visitImport(ImportStatement s)
     {
         debug (LOG)
         {
@@ -1745,6 +1628,45 @@ public:
         }
     }
 
+    if (!s)
+        return null;
+
+    mixin VisitStatement!void visit;
+    visit.VisitStatement(s);
+    return result;
+}
+
+///
+
+private extern (C++) final class Interpreter : Visitor
+{
+    alias visit = Visitor.visit;
+public:
+    InterState* istate;
+    CTFEGoal goal;
+    Expression result;
+    UnionExp* pue;              // storage for `result`
+
+    extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope
+    {
+        this.pue = pue;
+        this.istate = istate;
+        this.goal = goal;
+    }
+
+    // If e is EXP.throw_exception or EXP.cantExpression,
+    // set it to 'result' and returns true.
+    bool exceptionOrCant(Expression e)
+    {
+        if (exceptionOrCantInterpret(e))
+        {
+            // Make sure e is not pointing to a stack temporary
+            result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e;
+            return true;
+        }
+        return false;
+    }
+
     /******************************** Expression ***************************/
 
     override void visit(Expression e)
@@ -2519,7 +2441,7 @@ public:
     {
         debug (LOG)
         {
-            printf("%s ArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+            printf("%s ArrayLiteralExp::interpret() %s, %s\n", e.loc.toChars(), e.type.toChars(), e.toChars());
         }
         if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements
         {
@@ -2527,7 +2449,8 @@ public:
             return;
         }
 
-        Type tn = e.type.toBasetype().nextOf().toBasetype();
+        Type tb = e.type.toBasetype();
+        Type tn = tb.nextOf().toBasetype();
         bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct);
 
         auto basis = interpretRegion(e.basis, istate);
@@ -2536,6 +2459,7 @@ public:
 
         auto expsx = e.elements;
         size_t dim = expsx ? expsx.length : 0;
+
         for (size_t i = 0; i < dim; i++)
         {
             Expression exp = (*expsx)[i];
@@ -4049,6 +3973,8 @@ public:
      */
     private Expression interpretAssignToSlice(UnionExp* pue, BinExp e, Expression e1, Expression newval, bool isBlockAssignment)
     {
+        //printf("interpretAssignToSlice(e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars());
+
         dinteger_t lowerbound;
         dinteger_t upperbound;
         dinteger_t firstIndex;
@@ -4108,7 +4034,7 @@ public:
             return newval;
 
         // For slice assignment, we check that the lengths match.
-        if (!isBlockAssignment)
+        if (!isBlockAssignment && e1.type.ty != Tpointer)
         {
             const srclen = resolveArrayLength(newval);
             if (srclen != (upperbound - lowerbound))
@@ -4323,8 +4249,8 @@ public:
                 Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr)
                 {
                     Expressions* w = ae.elements;
-                    assert(ae.type.ty == Tsarray || ae.type.ty == Tarray);
-                    bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type);
+                    assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer);
+                    bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type);
                     for (size_t k = lwr; k < upr; k++)
                     {
                         if (!directblk && (*w)[k].op == EXP.arrayLiteral)
@@ -4848,25 +4774,6 @@ public:
                     result = CTFEExp.voidexp;
                 return;
             }
-            else if (fd.ident == Id._d_arraysetlengthT)
-            {
-                // In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`.
-                // The following code will rewrite it back to `ea.length = eb` and then interpret that expression.
-                assert(e.arguments.length == 2);
-
-                Expression ea = (*e.arguments)[0];
-                Expression eb = (*e.arguments)[1];
-
-                auto ale = ctfeEmplaceExp!ArrayLengthExp(e.loc, ea);
-                ale.type = Type.tsize_t;
-                AssignExp ae = ctfeEmplaceExp!AssignExp(e.loc, ale, eb);
-                ae.type = ea.type;
-
-                // if (global.params.verbose)
-                //     message("interpret  %s =>\n          %s", e.toChars(), ae.toChars());
-                result = interpretRegion(ae, istate);
-                return;
-            }
             else if (isArrayConstructionOrAssign(fd.ident))
             {
                 // In expressionsem.d, the following lowerings were performed:
@@ -5847,7 +5754,25 @@ public:
                 e2 = ue2.copy();
         }
 
-        *pue = ctfeCat(e.loc, e.type, e1, e2);
+        Expression prepareCatOperand(Expression exp)
+        {
+            /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an
+             * array. This is needed because interpreting the `CatExp` calls
+             * `Cat()`, which cannot handle concatenations between different
+             * types, except for strings and chars.
+             */
+            auto tb = e.type.toBasetype();
+            auto tbNext = tb.nextOf();
+            auto expTb = exp.type.toBasetype();
+
+            if (exp.type.implicitConvTo(tbNext) >= MATCH.convert &&
+                (tb.ty == Tarray || tb.ty == Tsarray) &&
+                (expTb.ty == Tarray || expTb.ty == Tsarray))
+                return new ArrayLiteralExp(exp.loc, e.type, exp);
+            return exp;
+        }
+
+        *pue = ctfeCat(e.loc, e.type, prepareCatOperand(e1), prepareCatOperand(e2));
         result = pue.exp();
 
         if (CTFEExp.isCantExp(result))
@@ -6208,15 +6133,18 @@ public:
         {
             printf("%s ThrowExpression::interpret()\n", te.loc.toChars());
         }
-        interpretThrow(te.e1, te.loc);
+        interpretThrow(result, te.e1, te.loc, istate);
     }
 
     override void visit(PtrExp e)
     {
+        // Called for both lvalues and rvalues
+        const lvalue = goal == CTFEGoal.LValue;
         debug (LOG)
         {
-            printf("%s PtrExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+            printf("%s PtrExp::interpret(%d) %s, %s\n", e.loc.toChars(), lvalue, e.type.toChars(), e.toChars());
         }
+
         // Check for int<->float and long<->double casts.
         if (auto soe1 = e.e1.isSymOffExp())
             if (soe1.offset == 0 && soe1.var.isVarDeclaration() && isFloatIntPaint(e.type, soe1.var.type))
@@ -6274,6 +6202,20 @@ public:
             return;
         }
 
+        if (!lvalue && result.isArrayLiteralExp() &&
+            result.type.isTypePointer())
+        {
+            /* A pointer variable can point to an array literal like `[3]`.
+             * Dereferencing it means accessing the first element value.
+             * Dereference it only if result should be an rvalue
+             */
+            auto ae = result.isArrayLiteralExp();
+            if (ae.elements.length == 1)
+            {
+                result = (*ae.elements)[0];
+                return;
+            }
+        }
         if (result.isStringExp() || result.isArrayLiteralExp())
             return;
 
@@ -6514,33 +6456,57 @@ public:
     {
         assert(0); // This should never be interpreted
     }
+}
 
-    /*********************************************
-     * Checks if the given expresion is a call to the runtime hook `id`.
-     * Params:
-     *    e = the expression to check
-     *    id = the identifier of the runtime hook
-     * Returns:
-     *    `e` cast to `CallExp` if it's the hook, `null` otherwise
-     */
-    private CallExp isRuntimeHook(Expression e, Identifier id)
+/// Interpret `throw <exp>` found at the specified location `loc`
+private
+void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate)
+{
+    incUsageCtfe(istate, loc);
+
+    Expression e = interpretRegion(exp, istate);
+    if (exceptionOrCantInterpret(e))
     {
-        if (auto ce = e.isCallExp())
+        // Make sure e is not pointing to a stack temporary
+        result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e;
+    }
+    else if (e.op == EXP.classReference)
+    {
+        result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp());
+    }
+    else
+    {
+        exp.error("to be thrown `%s` must be non-null", exp.toChars());
+        result = ErrorExp.get();
+    }
+}
+
+/*********************************************
+ * Checks if the given expresion is a call to the runtime hook `id`.
+ *
+ * Params:
+ *    e = the expression to check
+ *    id = the identifier of the runtime hook
+ * Returns:
+ *    `e` cast to `CallExp` if it's the hook, `null` otherwise
+ */
+public CallExp isRuntimeHook(Expression e, Identifier id)
+{
+    if (auto ce = e.isCallExp())
+    {
+        if (auto ve = ce.e1.isVarExp())
         {
-            if (auto ve = ce.e1.isVarExp())
+            if (auto fd = ve.var.isFuncDeclaration())
             {
-                if (auto fd = ve.var.isFuncDeclaration())
-                {
-                    // If `_d_HookTraceImpl` is found, resolve the underlying
-                    // hook and replace `e` and `fd` with it.
-                    removeHookTraceImpl(ce, fd);
-                    return fd.ident == id ? ce : null;
-                }
+                // If `_d_HookTraceImpl` is found, resolve the underlying hook
+                // and replace `e` and `fd` with it.
+                removeHookTraceImpl(ce, fd);
+                return fd.ident == id ? ce : null;
             }
         }
-
-        return null;
     }
+
+    return null;
 }
 
 /********************************************
@@ -6558,10 +6524,12 @@ Expression interpret(UnionExp* pue, Expression e, InterState* istate, CTFEGoal g
 {
     if (!e)
         return null;
+    //printf("+interpret() e : %s, %s\n", e.type.toChars(), e.toChars());
     scope Interpreter v = new Interpreter(pue, istate, goal);
     e.accept(v);
     Expression ex = v.result;
     assert(goal == CTFEGoal.Nothing || ex !is null);
+    //if (ex) printf("-interpret() ex: %s, %s\n", ex.type.toChars(), ex.toChars()); else printf("-interpret()\n");
     return ex;
 }
 
@@ -6608,34 +6576,135 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF
     return cast(Expression)memcpy(p, cast(void*)uexp, uexp.size);
 }
 
-/***********************************
- * Interpret the statement.
- * Params:
- *    pue = non-null pointer to temporary storage that can be used to store the return value
- *    s = Statement to interpret
- *    istate = context
- * Returns:
- *      NULL    continue to next statement
- *      EXP.cantExpression      cannot interpret statement at compile time
- *      !NULL   expression from return statement, or thrown exception
+private
+Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original)
+{
+    if (exps is original)
+    {
+        if (!original)
+            exps = new Expressions();
+        else
+            exps = original.copy();
+        ++ctfeGlobals.numArrayAllocs;
+    }
+    return exps;
+}
+
+/**
+ Given an expression e which is about to be returned from the current
+ function, generate an error if it contains pointers to local variables.
+
+ Only checks expressions passed by value (pointers to local variables
+ may already be stored in members of classes, arrays, or AAs which
+ were passed as mutable function parameters).
+ Returns:
+    true if it is safe to return, false if an error was generated.
  */
-Expression interpret(UnionExp* pue, Statement s, InterState* istate)
+private
+bool stopPointersEscaping(const ref Loc loc, Expression e)
 {
-    if (!s)
-        return null;
-    scope Interpreter v = new Interpreter(pue, istate, CTFEGoal.Nothing);
-    s.accept(v);
-    return v.result;
+    if (!e.type.hasPointers())
+        return true;
+    if (isPointer(e.type))
+    {
+        Expression x = e;
+        if (auto eaddr = e.isAddrExp())
+            x = eaddr.e1;
+        VarDeclaration v;
+        while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null)
+        {
+            if (v.storage_class & STC.ref_)
+            {
+                x = getValue(v);
+                if (auto eaddr = e.isAddrExp())
+                    eaddr.e1 = x;
+                continue;
+            }
+            if (ctfeGlobals.stack.isInCurrentFrame(v))
+            {
+                error(loc, "returning a pointer to a local stack variable");
+                return false;
+            }
+            else
+                break;
+        }
+        // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not
+        // pointing to a local struct or static array.
+    }
+    if (auto se = e.isStructLiteralExp())
+    {
+        return stopPointersEscapingFromArray(loc, se.elements);
+    }
+    if (auto ale = e.isArrayLiteralExp())
+    {
+        return stopPointersEscapingFromArray(loc, ale.elements);
+    }
+    if (auto aae = e.isAssocArrayLiteralExp())
+    {
+        if (!stopPointersEscapingFromArray(loc, aae.keys))
+            return false;
+        return stopPointersEscapingFromArray(loc, aae.values);
+    }
+    return true;
 }
 
-///
-Expression interpret(Statement s, InterState* istate)
+// Check all elements of an array for escaping local variables. Return false if error
+private
+bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems)
 {
-    UnionExp ue = void;
-    auto result = interpret(&ue, s, istate);
-    if (result == ue.exp())
-        result = ue.copy();
-    return result;
+    foreach (e; *elems)
+    {
+        if (e && !stopPointersEscaping(loc, e))
+            return false;
+    }
+    return true;
+}
+
+private
+Statement findGotoTarget(InterState* istate, Identifier ident)
+{
+    Statement target = null;
+    if (ident)
+    {
+        LabelDsymbol label = istate.fd.searchLabel(ident);
+        assert(label && label.statement);
+        LabelStatement ls = label.statement;
+        target = ls.gotoTarget ? ls.gotoTarget : ls.statement;
+    }
+    return target;
+}
+
+private
+ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest)
+{
+    debug (LOG)
+    {
+        printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars());
+    }
+    // Little sanity check to make sure it's really a Throwable
+    ClassReferenceExp boss = oldest.thrown;
+    const next = 5;                         // index of Throwable.next
+    assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
+    ClassReferenceExp collateral = newest.thrown;
+    if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException())
+    {
+        /* Find the index of the Error.bypassException field
+         */
+        auto bypass = next + 1;
+        if ((*collateral.value.elements)[bypass].type.ty == Tuns32)
+            bypass += 1;  // skip over _refcount field
+        assert((*collateral.value.elements)[bypass].type.ty == Tclass);
+
+        // The new exception bypass the existing chain
+        (*collateral.value.elements)[bypass] = boss;
+        return newest;
+    }
+    while ((*boss.value.elements)[next].op == EXP.classReference)
+    {
+        boss = (*boss.value.elements)[next].isClassReferenceExp();
+    }
+    (*boss.value.elements)[next] = collateral;
+    return oldest;
 }
 
 /**
@@ -6978,7 +7047,6 @@ private Expression copyRegionExp(Expression e)
         case EXP.null_:
         case EXP.void_:
         case EXP.symbolOffset:
-        case EXP.char_:
             break;
 
         case EXP.cantExpression:
index a5f7cd321da53c521e712c13788e91d806e28a58..149a5b1aeb551b23eb79145f7fcaeb9239ecca44 100644 (file)
@@ -767,7 +767,7 @@ extern (C++) final class Module : Package
         {
             filetype = FileType.c;
 
-            scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines);
+            scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines, &global.compileEnv);
             p.nextToken();
             checkCompiledImport();
             members = p.parseModule();
@@ -776,7 +776,9 @@ extern (C++) final class Module : Package
         }
         else
         {
-            scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink);
+            const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+            scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests);
+            p.transitionIn = global.params.vin;
             p.nextToken();
             p.parseModuleDeclaration();
             md = p.md;
@@ -1229,8 +1231,7 @@ extern (C++) final class Module : Package
         return this.importedFrom == this;
     }
 
-    // true if the module source file is directly
-    // listed in command line.
+    /// Returns: Whether this module is in the `core` package and has name `ident`
     bool isCoreModule(Identifier ident) nothrow
     {
         return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
@@ -1286,6 +1287,20 @@ extern (C++) final class Module : Package
         return _escapetable;
     }
 
+    /****************************
+     * A Singleton that loads core.stdc.config
+     * Returns:
+     *  Module of core.stdc.config, null if couldn't find it
+     */
+    extern (D) static Module loadCoreStdcConfig()
+    {
+        __gshared Module core_stdc_config;
+        auto pkgids = new Identifier[2];
+        pkgids[0] = Id.core;
+        pkgids[1] = Id.stdc;
+        return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config);
+    }
+
     /****************************
      * A Singleton that loads core.atomic
      * Returns:
@@ -1294,7 +1309,9 @@ extern (C++) final class Module : Package
     extern (D) static Module loadCoreAtomic()
     {
         __gshared Module core_atomic;
-        return loadModuleFromLibrary(core_atomic, Id.core, Id.atomic);
+        auto pkgids = new Identifier[1];
+        pkgids[0] = Id.core;
+        return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic);
     }
 
     /****************************
@@ -1305,26 +1322,26 @@ extern (C++) final class Module : Package
     extern (D) static Module loadStdMath()
     {
         __gshared Module std_math;
-        return loadModuleFromLibrary(std_math, Id.std, Id.math);
+        auto pkgids = new Identifier[1];
+        pkgids[0] = Id.std;
+        return loadModuleFromLibrary(std_math, pkgids, Id.math);
     }
 
     /**********************************
      * Load a Module from the library.
      * Params:
      *  mod = cached return value of this call
-     *  pkgid = package id
+     *  pkgids = package identifiers
      *  modid = module id
      * Returns:
      *  Module loaded, null if cannot load it
      */
-    private static Module loadModuleFromLibrary(ref Module mod, Identifier pkgid, Identifier modid)
+    extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid)
     {
         if (mod)
             return mod;
 
-        auto ids = new Identifier[1];
-        ids[0] = pkgid;
-        auto imp = new Import(Loc.initial, ids[], modid, null, true);
+        auto imp = new Import(Loc.initial, pkgids[], modid, null, true);
         // Module.load will call fatal() if there's no module available.
         // Gag the error here, pushing the error handling to the caller.
         const errors = global.startGagging();
index 88e8996ae3aafda278e3a2bf2692da70c8d99f7a..7674f775e3e89fc32b0160830ca9ae7b76f87d38 100644 (file)
@@ -5184,7 +5184,7 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of
 
     scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1,
         global.errorSink,
-        global.vendor, global.versionNumber());
+        &global.compileEnv);
     OutBuffer res;
     const(char)* lastp = cast(char*)buf[].ptr;
     //printf("highlightCode2('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr);
index ab422fd0cf6024d71c02b58ba92478459139075a..95cfec9c2b9eeb7903ff5324235c8d720e30f820 100644 (file)
@@ -813,4 +813,14 @@ extern (C++) struct Scope
     {
         return this.intypeof || this.flags & SCOPE.compile;
     }
+
+
+    /**
+     * Returns: true if the code needs to go all the way through to code generation.
+     * This implies things like needing lowering to simpler forms.
+     */
+    extern (D) bool needsCodegen()
+    {
+        return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0;
+    }
 }
index 3268d5667de88675c4df6f5dd9eddadc6c5b358a..49b98411ad37325e398cfa4d3f63e490d52f1af8 100644 (file)
@@ -13,6 +13,8 @@
 
 module dmd.dstruct;
 
+import core.stdc.stdio;
+
 import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
@@ -75,7 +77,7 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t)
     {
         if (sc.intypeof)
             return;
-        if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock))
+        if (!sc.needsCodegen())
             return;
     }
 
@@ -567,7 +569,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
  * Returns:
  *      true if it's all binary 0
  */
-private bool _isZeroInit(Expression exp)
+bool _isZeroInit(Expression exp)
 {
     switch (exp.op)
     {
@@ -575,20 +577,22 @@ private bool _isZeroInit(Expression exp)
             return exp.toInteger() == 0;
 
         case EXP.null_:
-        case EXP.false_:
             return true;
 
         case EXP.structLiteral:
         {
-            auto sle = cast(StructLiteralExp) exp;
+            auto sle = exp.isStructLiteralExp();
+            if (sle.sd.isNested())
+                return false;
+            const isCstruct = sle.sd.isCsymbol();  // C structs are default initialized to all zeros
             foreach (i; 0 .. sle.sd.fields.length)
             {
                 auto field = sle.sd.fields[i];
                 if (field.type.size(field.loc))
                 {
-                    auto e = (*sle.elements)[i];
+                    auto e = sle.elements && i < sle.elements.length ? (*sle.elements)[i] : null;
                     if (e ? !_isZeroInit(e)
-                          : !field.type.isZeroInit(field.loc))
+                          : !isCstruct && !field.type.isZeroInit(field.loc))
                         return false;
                 }
             }
index e7ce93ee067cc3fd351d120282ae43cfb20ee27a..23733135fd8de0afd507022a823506562dfaa18e 100644 (file)
@@ -1407,7 +1407,7 @@ extern (C++) class Dsymbol : ASTNode
     inout(CPPNamespaceDeclaration)     isCPPNamespaceDeclaration()     inout { return null; }
     inout(VisibilityDeclaration)       isVisibilityDeclaration()       inout { return null; }
     inout(OverloadSet)                 isOverloadSet()                 inout { return null; }
-    inout(CompileDeclaration)          isCompileDeclaration()          inout { return null; }
+    inout(MixinDeclaration)            isMixinDeclaration()            inout { return null; }
     inout(StaticAssert)                isStaticAssert()                inout { return null; }
     inout(StaticIfDeclaration)         isStaticIfDeclaration()         inout { return null; }
 }
index 039a28871b3bde768b105f53e43af4372ba63b11..96fa8fdd6400ffa3313e87e86952b4a2c2acfae4 100644 (file)
@@ -321,7 +321,7 @@ public:
     virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; }
     virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; }
     virtual OverloadSet *isOverloadSet() { return NULL; }
-    virtual CompileDeclaration *isCompileDeclaration() { return NULL; }
+    virtual MixinDeclaration *isMixinDeclaration() { return NULL; }
     virtual StaticAssert *isStaticAssert() { return NULL; }
     virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; }
     void accept(Visitor *v) override { v->visit(this); }
index 0f0ed2adec796d86ce150b414c15ccde59942b98..506946fe4586db245c0cb070811eff7642868d2a 100644 (file)
@@ -1321,6 +1321,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         if (dsym.errors)
             return;
 
+        if (!(global.params.bitfields || sc.flags & SCOPE.Cfile))
+            dsym.error("use -preview=bitfields for bitfield support");
+
         if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
         {
             dsym.error("- bit-field must be member of struct, union, or class");
@@ -1923,9 +1926,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         attribSemantic(sfd);
     }
 
-    private Dsymbols* compileIt(CompileDeclaration cd)
+    private Dsymbols* compileIt(MixinDeclaration cd)
     {
-        //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
+        //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
         OutBuffer buf;
         if (expressionsToString(buf, sc, cd.exps))
             return null;
@@ -1934,7 +1937,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         const len = buf.length;
         buf.writeByte(0);
         const str = buf.extractSlice()[0 .. len];
-        scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink);
+        const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+        auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut);
+        scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
+        p.transitionIn = global.params.vin;
         p.nextToken();
 
         auto d = p.parseDeclDefs(0);
@@ -1952,9 +1958,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
     /***********************************************************
      * https://dlang.org/spec/module.html#mixin-declaration
      */
-    override void visit(CompileDeclaration cd)
+    override void visit(MixinDeclaration cd)
     {
-        //printf("CompileDeclaration::semantic()\n");
+        //printf("MixinDeclaration::semantic()\n");
         if (!cd.compiled)
         {
             cd.decl = compileIt(cd);
@@ -2252,32 +2258,39 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         {
             /* C11 6.7.2.2
              */
-            assert(ed.memtype);
-            int nextValue = 0;        // C11 6.7.2.2-3 first member value defaults to 0
+            Type commonType = ed.memtype;
+            if (!commonType)
+                commonType = Type.tint32;
+            ulong nextValue = 0;        // C11 6.7.2.2-3 first member value defaults to 0
 
             // C11 6.7.2.2-2 value must be representable as an int.
             // The sizemask represents all values that int will fit into,
             // from 0..uint.max.  We want to cover int.min..uint.max.
-            const mask = Type.tint32.sizemask();
-            IntRange ir = IntRange(SignExtendedNumber(~(mask >> 1), true),
-                                   SignExtendedNumber(mask));
+            IntRange ir = IntRange.fromType(commonType);
 
-            void emSemantic(EnumMember em, ref int nextValue)
+            void emSemantic(EnumMember em, ref ulong nextValue)
             {
                 static void errorReturn(EnumMember em)
                 {
+                    em.value = ErrorExp.get();
                     em.errors = true;
                     em.semanticRun = PASS.semanticdone;
                 }
 
                 em.semanticRun = PASS.semantic;
-                em.type = Type.tint32;
+                em.type = commonType;
                 em._linkage = LINK.c;
                 em.storage_class |= STC.manifest;
                 if (em.value)
                 {
                     Expression e = em.value;
                     assert(e.dyncast() == DYNCAST.expression);
+
+                    /* To merge the type of e with commonType, add 0 of type commonType
+                     */
+                    if (!ed.memtype)
+                        e = new AddExp(em.loc, e, new IntegerExp(em.loc, 0, commonType));
+
                     e = e.expressionSemantic(sc);
                     e = resolveProperties(sc, e);
                     e = e.integralPromotions(sc);
@@ -2291,14 +2304,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                         em.error("enum member must be an integral constant expression, not `%s` of type `%s`", e.toChars(), e.type.toChars());
                         return errorReturn(em);
                     }
-                    if (!ir.contains(getIntRange(ie)))
+                    if (ed.memtype && !ir.contains(getIntRange(ie)))
                     {
                         // C11 6.7.2.2-2
-                        em.error("enum member value `%s` does not fit in an `int`", e.toChars());
+                        em.error("enum member value `%s` does not fit in `%s`", e.toChars(), commonType.toChars());
                         return errorReturn(em);
                     }
-                    nextValue = cast(int)ie.toInteger();
-                    em.value = new IntegerExp(em.loc, nextValue, Type.tint32);
+                    nextValue = ie.toInteger();
+                    if (!ed.memtype)
+                        commonType = e.type;
+                    em.value = new IntegerExp(em.loc, nextValue, commonType);
                 }
                 else
                 {
@@ -2306,17 +2321,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     bool first = (em == (*em.ed.members)[0]);
                     if (!first)
                     {
-                        import core.checkedint : adds;
-                        bool overflow;
-                        nextValue = adds(nextValue, 1, overflow);
-                        if (overflow)
+                        Expression max = getProperty(commonType, null, em.loc, Id.max, 0);
+                        if (nextValue == max.toInteger())
                         {
-                            em.error("initialization with `%d+1` causes overflow for type `int`", nextValue - 1);
+                            em.error("initialization with `%s+1` causes overflow for type `%s`", max.toChars(), commonType.toChars());
                             return errorReturn(em);
                         }
+                        nextValue += 1;
                     }
-                    em.value = new IntegerExp(em.loc, nextValue, Type.tint32);
+                    em.value = new IntegerExp(em.loc, nextValue, commonType);
                 }
+                em.type = commonType;
                 em.semanticRun = PASS.semanticdone;
             }
 
@@ -2325,6 +2340,21 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 if (EnumMember em = s.isEnumMember())
                     emSemantic(em, nextValue);
             });
+
+            if (!ed.memtype)
+            {
+                // cast all members to commonType
+                ed.members.foreachDsymbol( (s)
+                {
+                    if (EnumMember em = s.isEnumMember())
+                    {
+                        em.type = commonType;
+                        em.value = em.value.castTo(sc, commonType);
+                    }
+                });
+            }
+
+            ed.memtype = commonType;
             ed.semanticRun = PASS.semanticdone;
             return;
         }
@@ -4662,7 +4692,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         {
             sd.visibility = sc.visibility;
 
-            sd.alignment = sc.alignment();
+            if (sd.alignment.isUnknown())       // can be set already by `struct __declspec(align(N)) Tag { ... }`
+                sd.alignment = sc.alignment();
 
             sd.storage_class |= sc.stc;
             if (sd.storage_class & STC.abstract_)
@@ -7228,3 +7259,83 @@ PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args)
     else
         return PINLINE.never;
 }
+
+/***************************************************
+ * Set up loc for a parse of a mixin. Append the input text to the mixin.
+ * Params:
+ *      input = mixin text
+ *      loc = location to adjust
+ *      mixinOut = sink for mixin text data
+ * Returns:
+ *      adjusted loc suitable for Parser
+ */
+
+Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOut)
+{
+    Loc result;
+    if (mixinOut.doOutput)
+    {
+        const lines = mixinOut.bufferLines;
+        writeMixin(input, loc, mixinOut.bufferLines, *mixinOut.buffer);
+        result = Loc(mixinOut.name.ptr, lines + 2, loc.charnum);
+    }
+    else if (loc.filename)
+    {
+        /* Create a pseudo-filename for the mixin string, as it may not even exist
+         * in the source file.
+         */
+        auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1;
+        char* filename = cast(char*)mem.xmalloc(len);
+        snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
+        result = Loc(filename, loc.linnum, loc.charnum);
+    }
+    else
+        result = loc;
+    return result;
+}
+
+/**************************************
+ * Append source code text to output for better debugging.
+ * Canonicalize line endings.
+ * Params:
+ *      s = source code text
+ *      loc = location of source code text
+ *      lines = line count to update
+ *      output = sink for output
+ */
+private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref OutBuffer buf)
+{
+    buf.writestring("// expansion at ");
+    buf.writestring(loc.toChars());
+    buf.writenl();
+
+    ++lines;
+
+    // write by line to create consistent line endings
+    size_t lastpos = 0;
+    for (size_t i = 0; i < s.length; ++i)
+    {
+        // detect LF and CRLF
+        const c = s[i];
+        if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
+        {
+            buf.writestring(s[lastpos .. i]);
+            buf.writenl();
+            ++lines;
+            if (c == '\r')
+                ++i;
+            lastpos = i + 1;
+        }
+    }
+
+    if(lastpos < s.length)
+        buf.writestring(s[lastpos .. $]);
+
+    if (s.length == 0 || s[$-1] != '\n')
+    {
+        buf.writenl(); // ensure empty line after expansion
+        ++lines;
+    }
+    buf.writenl();
+    ++lines;
+}
index ad3a6d4dd54f9e9af4904691d87ad341e5f438a7..ef743d60fd9616ff1efd9c4f6c08131c45e93d98 100644 (file)
@@ -741,9 +741,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
 
     const(char)* toCharsMaybeConstraints(bool includeConstraints) const
     {
-        if (literal)
-            return Dsymbol.toChars();
-
         OutBuffer buf;
         HdrGenState hgs;
 
@@ -1858,19 +1855,16 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
                         Type taai;
                         if (argtype.ty == Tarray && (prmtype.ty == Tsarray || prmtype.ty == Taarray && (taai = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
                         {
-                            if (farg.op == EXP.string_)
+                            if (StringExp se = farg.isStringExp())
                             {
-                                StringExp se = cast(StringExp)farg;
                                 argtype = se.type.nextOf().sarrayOf(se.len);
                             }
-                            else if (farg.op == EXP.arrayLiteral)
+                            else if (ArrayLiteralExp ae = farg.isArrayLiteralExp())
                             {
-                                ArrayLiteralExp ae = cast(ArrayLiteralExp)farg;
                                 argtype = ae.type.nextOf().sarrayOf(ae.elements.length);
                             }
-                            else if (farg.op == EXP.slice)
+                            else if (SliceExp se = farg.isSliceExp())
                             {
-                                SliceExp se = cast(SliceExp)farg;
                                 if (Type tsa = toStaticArrayType(se))
                                     argtype = tsa;
                             }
@@ -2283,18 +2277,23 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
         Declaration d;
         VarDeclaration v = null;
 
-        if (ea && ea.op == EXP.type)
-            ta = ea.type;
-        else if (ea && ea.op == EXP.scope_)
-            sa = (cast(ScopeExp)ea).sds;
-        else if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_))
-            sa = (cast(ThisExp)ea).var;
-        else if (ea && ea.op == EXP.function_)
+        if (ea)
         {
-            if ((cast(FuncExp)ea).td)
-                sa = (cast(FuncExp)ea).td;
-            else
-                sa = (cast(FuncExp)ea).fd;
+            if (ea.op == EXP.type)
+                ta = ea.type;
+            else if (auto se = ea.isScopeExp())
+                sa = se.sds;
+            else if (auto te = ea.isThisExp())
+                sa = te.var;
+            else if (auto se = ea.isSuperExp())
+                sa = se.var;
+            else if (auto fe = ea.isFuncExp())
+            {
+                if (fe.td)
+                    sa = fe.td;
+                else
+                    sa = fe.fd;
+            }
         }
 
         if (ta)
@@ -3856,9 +3855,9 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
                 if (tparam.ty == Tsarray)
                 {
                     TypeSArray tsa = cast(TypeSArray)tparam;
-                    if (tsa.dim.op == EXP.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter)
+                    if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter)
                     {
-                        Identifier id = (cast(VarExp)tsa.dim).var.ident;
+                        Identifier id = tsa.dim.isVarExp().var.ident;
                         i = templateIdentifierLookup(id, parameters);
                         assert(i != IDX_NOTFOUND);
                         tp = (*parameters)[i];
@@ -4250,12 +4249,12 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
                         /* If it is one of the template parameters for this template,
                          * we should not attempt to interpret it. It already has a value.
                          */
-                        if (e2.op == EXP.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter))
+                        if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter))
                         {
                             /*
                              * (T:Number!(e2), int e2)
                              */
-                            j = templateIdentifierLookup((cast(VarExp)e2).var.ident, parameters);
+                            j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters);
                             if (j != IDX_NOTFOUND)
                                 goto L1;
                             // The template parameter was not from this template
@@ -5236,7 +5235,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
         override void visit(DotTemplateInstanceExp e)
         {
             //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(cast(UnaExp)e);
+            visit(e.isUnaExp());
             if (!result && e.ti.tiargs)
             {
                 foreach (oa; *e.ti.tiargs)
@@ -5254,7 +5253,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
         override void visit(CallExp e)
         {
             //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(cast(UnaExp)e);
+            visit(e.isUnaExp());
             if (!result && e.arguments)
             {
                 foreach (ea; *e.arguments)
@@ -5269,7 +5268,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
         override void visit(CastExp e)
         {
             //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(cast(UnaExp)e);
+            visit(e.isUnaExp());
             // e.to can be null for cast() with no type
             if (!result && e.to)
                 result = e.to.reliesOnTemplateParameters(tparams);
@@ -5278,7 +5277,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
         override void visit(SliceExp e)
         {
             //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(cast(UnaExp)e);
+            visit(e.isUnaExp());
             if (!result && e.lwr)
                 e.lwr.accept(this);
             if (!result && e.upr)
@@ -5296,7 +5295,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
         override void visit(ArrayExp e)
         {
             //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(cast(UnaExp)e);
+            visit(e.isUnaExp());
             if (!result && e.arguments)
             {
                 foreach (ea; *e.arguments)
@@ -5317,7 +5316,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
             //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
             e.econd.accept(this);
             if (!result)
-                visit(cast(BinExp)e);
+                visit(e.isBinExp());
         }
     }
 
@@ -6727,7 +6726,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                     ea = ea.expressionSemantic(sc);
 
                     // must not interpret the args, excepting template parameters
-                    if (ea.op != EXP.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter))
+                    if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter))
                     {
                         ea = ea.optimize(WANTvalue);
                     }
@@ -6738,13 +6737,13 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                     ea = ea.expressionSemantic(sc);
                     sc = sc.endCTFE();
 
-                    if (ea.op == EXP.variable)
+                    if (auto varExp = ea.isVarExp())
                     {
                         /* If the parameter is a function that is not called
                          * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
                          * then it is a dsymbol, not the return value of `func()`
                          */
-                        Declaration vd = (cast(VarExp)ea).var;
+                        Declaration vd = varExp.var;
                         if (auto fd = vd.isFuncDeclaration())
                         {
                             sa = fd;
@@ -6767,10 +6766,9 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                     }
                 }
                 //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
-                if (ea.op == EXP.tuple)
+                if (TupleExp te = ea.isTupleExp())
                 {
                     // Expand tuple
-                    TupleExp te = cast(TupleExp)ea;
                     size_t dim = te.exps.length;
                     tiargs.remove(j);
                     if (dim)
@@ -6796,12 +6794,11 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                 }
                 if (ea.op == EXP.scope_)
                 {
-                    sa = (cast(ScopeExp)ea).sds;
+                    sa = ea.isScopeExp().sds;
                     goto Ldsym;
                 }
-                if (ea.op == EXP.function_)
+                if (FuncExp fe = ea.isFuncExp())
                 {
-                    FuncExp fe = cast(FuncExp)ea;
                     /* A function literal, that is passed to template and
                      * already semanticed as function pointer, never requires
                      * outer frame. So convert it to global function is valid.
@@ -6823,23 +6820,23 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                 if (ea.op == EXP.dotVariable && !(flags & 1))
                 {
                     // translate expression to dsymbol.
-                    sa = (cast(DotVarExp)ea).var;
+                    sa = ea.isDotVarExp().var;
                     goto Ldsym;
                 }
-                if (ea.op == EXP.template_)
+                if (auto te = ea.isTemplateExp())
                 {
-                    sa = (cast(TemplateExp)ea).td;
+                    sa = te.td;
                     goto Ldsym;
                 }
                 if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1))
                 {
                     // translate expression to dsymbol.
-                    sa = (cast(DotTemplateExp)ea).td;
+                    sa = ea.isDotTemplateExp().td;
                     goto Ldsym;
                 }
-                if (ea.op == EXP.dot)
+                if (auto de = ea.isDotExp())
                 {
-                    if (auto se = (cast(DotExp)ea).e2.isScopeExp())
+                    if (auto se = de.e2.isScopeExp())
                     {
                         sa = se.sds;
                         goto Ldsym;
@@ -7340,22 +7337,22 @@ extern (C++) class TemplateInstance : ScopeDsymbol
             Tuple va = isTuple(o);
             if (ea)
             {
-                if (ea.op == EXP.variable)
+                if (auto ve = ea.isVarExp())
                 {
-                    sa = (cast(VarExp)ea).var;
+                    sa = ve.var;
                     goto Lsa;
                 }
-                if (ea.op == EXP.this_)
+                if (auto te = ea.isThisExp())
                 {
-                    sa = (cast(ThisExp)ea).var;
+                    sa = te.var;
                     goto Lsa;
                 }
-                if (ea.op == EXP.function_)
+                if (auto fe = ea.isFuncExp())
                 {
-                    if ((cast(FuncExp)ea).td)
-                        sa = (cast(FuncExp)ea).td;
+                    if (fe.td)
+                        sa = fe.td;
                     else
-                        sa = (cast(FuncExp)ea).fd;
+                        sa = fe.fd;
                     goto Lsa;
                 }
                 // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
@@ -7727,13 +7724,13 @@ bool definitelyValueParameter(Expression e)
      */
 
     // x.y.f cannot be a value
-    FuncDeclaration f = (cast(DotVarExp)e).var.isFuncDeclaration();
+    FuncDeclaration f = e.isDotVarExp().var.isFuncDeclaration();
     if (f)
         return false;
 
     while (e.op == EXP.dotVariable)
     {
-        e = (cast(DotVarExp)e).e1;
+        e = e.isDotVarExp().e1;
     }
     // this.x.y and super.x.y couldn't possibly be valid values.
     if (e.op == EXP.this_ || e.op == EXP.super_)
@@ -7747,7 +7744,7 @@ bool definitelyValueParameter(Expression e)
     if (e.op != EXP.variable)
         return true;
 
-    VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+    VarDeclaration v = e.isVarExp().var.isVarDeclaration();
     // func.x.y is not an alias
     if (!v)
         return true;
@@ -8242,10 +8239,15 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ
         Type ta = isType(oarg);
         RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
         Expression ea = isExpression(oarg);
-        if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_))
-            sa = (cast(ThisExp)ea).var;
-        else if (ea && ea.op == EXP.scope_)
-            sa = (cast(ScopeExp)ea).sds;
+        if (ea)
+        {
+            if (auto te = ea.isThisExp())
+                sa = te.var;
+            else if (auto se = ea.isSuperExp())
+                sa = se.var;
+            else if (auto se = ea.isScopeExp())
+                sa = se.sds;
+        }
         if (sa)
         {
             if ((cast(Dsymbol)sa).isAggregateDeclaration())
index 7c3ff4bccc2ca87842ac8164f82e3d26f6cd207f..f00b8dba86c310936a0f75343756d9668f4d6589 100644 (file)
@@ -84,9 +84,9 @@ extern(C++) void genCppHdrFiles(ref Modules ms)
         m.accept(v);
 
     if (global.params.cxxhdr.fullOutput)
-        buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber());
+        buf.printf("// Automatically generated by %s Compiler v%d", global.compileEnv.vendor.ptr, global.versionNumber());
     else
-        buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr);
+        buf.printf("// Automatically generated by %s Compiler", global.compileEnv.vendor.ptr);
 
     buf.writenl();
     buf.writenl();
index f107f7b09b3a24f197ef7a546f2e2d373b1da0db..287dc4963e9e674cd5b4e8d324bc3124357dfe9e 100644 (file)
@@ -66,6 +66,14 @@ class ErrorSinkCompiler : ErrorSink
         vdeprecationSupplemental(loc, format, ap);
         va_end(ap);
     }
+
+    void message(const ref Loc loc, const(char)* format, ...)
+    {
+        va_list ap;
+        va_start(ap, format);
+        vmessage(loc, format, ap);
+        va_end(ap);
+    }
 }
 
 
index b519db7e9bc08bbc2a4ad227b6dc970694fae678..e57c2b6e388d62cdd5ca2aa8f24698d4e62e5416 100644 (file)
@@ -27,6 +27,8 @@ abstract class ErrorSink
 
     void warning(const ref Loc loc, const(char)* format, ...);
 
+    void message(const ref Loc loc, const(char)* format, ...);
+
     void deprecation(const ref Loc loc, const(char)* format, ...);
 
     void deprecationSupplemental(const ref Loc loc, const(char)* format, ...);
@@ -47,6 +49,8 @@ class ErrorSinkNull : ErrorSink
 
     void warning(const ref Loc loc, const(char)* format, ...) { }
 
+    void message(const ref Loc loc, const(char)* format, ...) { }
+
     void deprecation(const ref Loc loc, const(char)* format, ...) { }
 
     void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { }
@@ -117,5 +121,21 @@ class ErrorSinkStderr : ErrorSink
         va_end(ap);
     }
 
+    void message(const ref Loc loc, const(char)* format, ...)
+    {
+        const p = loc.toChars();
+        if (*p)
+        {
+            fprintf(stderr, "%s: ", p);
+            //mem.xfree(cast(void*)p); // loc should provide the free()
+        }
+
+        va_list ap;
+        va_start(ap, format);
+        vfprintf(stderr, format, ap);
+        fputc('\n', stderr);
+        va_end(ap);
+    }
+
     void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { }
 }
index 420fa7f80bbb816dc87c0efed8cd46c1158d513d..4f1edaa4d05bfb1ca694f79a4c9b7a1b34c63861 100644 (file)
@@ -2377,7 +2377,7 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
         foreach (u, p; f.parameterList)
         {
             auto v = (*funcdecl.parameters)[u];
-            if (!v.isScope() && inferScope(v))
+            if (!v.isScope() && v.type.hasPointers() && inferScope(v))
             {
                 //printf("Inferring scope for %s\n", v.toChars());
                 p.storageClass |= STC.scope_ | STC.scopeinferred;
index df5e9ddc98ef33810312e0d2c2c796aa22e76001..067d22fe1306148d1235e9fce6fe5b234661e120 100644 (file)
@@ -720,20 +720,21 @@ enum WANTexpand = 1;    // expand const/immutable variables if possible
  */
 extern (C++) abstract class Expression : ASTNode
 {
-    const EXP op;   // to minimize use of dynamic_cast
-    ubyte size;     // # of bytes in Expression so we can copy() it
-    bool parens;    // if this is a parenthesized expression
     Type type;      // !=null means that semantic() has been run
     Loc loc;        // file location
+    const EXP op;   // to minimize use of dynamic_cast
+    bool parens;    // if this is a parenthesized expression
 
-    extern (D) this(const ref Loc loc, EXP op, int size) scope
+    extern (D) this(const ref Loc loc, EXP op) scope
     {
         //printf("Expression::Expression(op = %d) this = %p\n", op, this);
         this.loc = loc;
         this.op = op;
-        this.size = cast(ubyte)size;
     }
 
+    /// Returns: class instance size of this expression (implemented manually because `extern(C++)`)
+    final size_t size() nothrow @nogc pure @safe const { return expSize[op]; }
+
     static void _init()
     {
         CTFEExp.cantexp = new CTFEExp(EXP.cantExpression);
@@ -1219,12 +1220,15 @@ extern (C++) abstract class Expression : ASTNode
             return false;
 
         // If the call has a pure parent, then the called func must be pure.
-        if (!f.isPure() && checkImpure(sc))
+        if (!f.isPure() && checkImpure(sc, loc, null, f))
         {
             error("`pure` %s `%s` cannot call impure %s `%s`",
                 sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
                 f.toPrettyChars());
 
+            if (!f.isDtorDeclaration())
+                errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_);
+
             checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
             return true;
         }
@@ -1355,7 +1359,7 @@ extern (C++) abstract class Expression : ASTNode
             if (v.ident == Id.gate)
                 return false;
 
-            if (checkImpure(sc))
+            if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v))
             {
                 error("`pure` %s `%s` cannot access mutable static data `%s`",
                     sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
@@ -1431,11 +1435,11 @@ extern (C++) abstract class Expression : ASTNode
     Check if sc.func is impure or can be made impure.
     Returns true on error, i.e. if sc.func is pure and cannot be made impure.
     */
-    private static bool checkImpure(Scope* sc)
+    private static bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0)
     {
         return sc.func && (isRootTraitsCompilesScope(sc)
                 ? sc.func.isPureBypassingInference() >= PURE.weak
-                : sc.func.setImpure());
+                : sc.func.setImpure(loc, fmt, arg0));
     }
 
     /*********************************************
@@ -1484,7 +1488,8 @@ extern (C++) abstract class Expression : ASTNode
                 error("`@safe` %s `%s` cannot call `@system` %s `%s`",
                     sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
                     prettyChars);
-                f.errorSupplementalInferredSafety(/*max depth*/ 10, /*deprecation*/ false);
+                if (!f.isDtorDeclaration)
+                    errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe);
                 .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
 
                 checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
@@ -1498,7 +1503,7 @@ extern (C++) abstract class Expression : ASTNode
             if (sc.func.isSafeBypassingInference())
             {
                 .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars());
-                errorSupplementalInferredSafety(f, 10, true);
+                errorSupplementalInferredAttr(f, 10, true, STC.safe);
             }
             else if (!sc.func.safetyViolation)
             {
@@ -1528,7 +1533,7 @@ extern (C++) abstract class Expression : ASTNode
 
         if (!f.isNogc())
         {
-            if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGC())
+            if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
             {
                 if (loc.linnum == 0) // e.g. implicitly generated dtor
                     loc = sc.func.loc;
@@ -1537,10 +1542,15 @@ extern (C++) abstract class Expression : ASTNode
                 // so don't print anything to avoid double error messages.
                 if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
                     || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
-                    || f.ident == Id._d_newclassT))
+                    || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+                {
                     error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
                         sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
 
+                    if (!f.isDtorDeclaration)
+                        f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
+                }
+
                 checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
 
                 return true;
@@ -1775,6 +1785,7 @@ extern (C++) abstract class Expression : ASTNode
         inout(PostExp)      isPostExp()  { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; }
         inout(PreExp)       isPreExp()   { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; }
         inout(AssignExp)    isAssignExp()    { return op == EXP.assign ? cast(typeof(return))this : null; }
+        inout(LoweredAssignExp)    isLoweredAssignExp()    { return op == EXP.loweredAssignExp ? cast(typeof(return))this : null; }
         inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; }
         inout(BlitExp)      isBlitExp()      { return op == EXP.blit ? cast(typeof(return))this : null; }
         inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; }
@@ -1866,7 +1877,7 @@ extern (C++) final class IntegerExp : Expression
 
     extern (D) this(const ref Loc loc, dinteger_t value, Type type)
     {
-        super(loc, EXP.int64, __traits(classInstanceSize, IntegerExp));
+        super(loc, EXP.int64);
         //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
         assert(type);
         if (!type.isscalar())
@@ -1882,7 +1893,7 @@ extern (C++) final class IntegerExp : Expression
 
     extern (D) this(dinteger_t value)
     {
-        super(Loc.initial, EXP.int64, __traits(classInstanceSize, IntegerExp));
+        super(Loc.initial, EXP.int64);
         this.type = Type.tint32;
         this.value = cast(int)value;
     }
@@ -2082,7 +2093,7 @@ extern (C++) final class ErrorExp : Expression
 {
     private extern (D) this()
     {
-        super(Loc.initial, EXP.error, __traits(classInstanceSize, ErrorExp));
+        super(Loc.initial, EXP.error);
         type = Type.terror;
     }
 
@@ -2130,7 +2141,7 @@ extern (C++) final class VoidInitExp : Expression
 
     extern (D) this(VarDeclaration var)
     {
-        super(var.loc, EXP.void_, __traits(classInstanceSize, VoidInitExp));
+        super(var.loc, EXP.void_);
         this.var = var;
         this.type = var.type;
     }
@@ -2156,7 +2167,7 @@ extern (C++) final class RealExp : Expression
 
     extern (D) this(const ref Loc loc, real_t value, Type type)
     {
-        super(loc, EXP.float64, __traits(classInstanceSize, RealExp));
+        super(loc, EXP.float64);
         //printf("RealExp::RealExp(%Lg)\n", value);
         this.value = value;
         this.type = type;
@@ -2239,7 +2250,7 @@ extern (C++) final class ComplexExp : Expression
 
     extern (D) this(const ref Loc loc, complex_t value, Type type)
     {
-        super(loc, EXP.complex80, __traits(classInstanceSize, ComplexExp));
+        super(loc, EXP.complex80);
         this.value = value;
         this.type = type;
         //printf("ComplexExp::ComplexExp(%s)\n", toChars());
@@ -2330,7 +2341,7 @@ extern (C++) class IdentifierExp : Expression
 
     extern (D) this(const ref Loc loc, Identifier ident) scope
     {
-        super(loc, EXP.identifier, __traits(classInstanceSize, IdentifierExp));
+        super(loc, EXP.identifier);
         this.ident = ident;
     }
 
@@ -2383,7 +2394,7 @@ extern (C++) final class DsymbolExp : Expression
 
     extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true)
     {
-        super(loc, EXP.dSymbol, __traits(classInstanceSize, DsymbolExp));
+        super(loc, EXP.dSymbol);
         this.s = s;
         this.hasOverloads = hasOverloads;
     }
@@ -2413,13 +2424,13 @@ extern (C++) class ThisExp : Expression
 
     extern (D) this(const ref Loc loc)
     {
-        super(loc, EXP.this_, __traits(classInstanceSize, ThisExp));
+        super(loc, EXP.this_);
         //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
     }
 
     this(const ref Loc loc, const EXP tok)
     {
-        super(loc, tok, __traits(classInstanceSize, ThisExp));
+        super(loc, tok);
         //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
     }
 
@@ -2485,7 +2496,7 @@ extern (C++) final class NullExp : Expression
 {
     extern (D) this(const ref Loc loc, Type type = null) scope
     {
-        super(loc, EXP.null_, __traits(classInstanceSize, NullExp));
+        super(loc, EXP.null_);
         this.type = type;
     }
 
@@ -2529,6 +2540,8 @@ extern (C++) final class NullExp : Expression
  */
 extern (C++) final class StringExp : Expression
 {
+    char postfix = NoPostfix;   // 'c', 'w', 'd'
+    OwnedBy ownedByCtfe = OwnedBy.code;
     private union
     {
         char* string;   // if sz == 1
@@ -2537,14 +2550,22 @@ extern (C++) final class StringExp : Expression
     }                   // (const if ownedByCtfe == OwnedBy.code)
     size_t len;         // number of code units
     ubyte sz = 1;       // 1: char, 2: wchar, 4: dchar
-    ubyte committed;    // !=0 if type is committed
+
+    /**
+     *  Whether the string literal's type is fixed
+     *  Example:
+     *  ---
+     *  wstring x = "abc"; // OK, string literal is flexible
+     *  wstring y = cast(string) "abc"; // Error: type was committed after cast
+     *  ---
+     */
+    bool committed;
+
     enum char NoPostfix = 0;
-    char postfix = NoPostfix;   // 'c', 'w', 'd'
-    OwnedBy ownedByCtfe = OwnedBy.code;
 
     extern (D) this(const ref Loc loc, const(void)[] string) scope
     {
-        super(loc, EXP.string_, __traits(classInstanceSize, StringExp));
+        super(loc, EXP.string_);
         this.string = cast(char*)string.ptr; // note that this.string should be const
         this.len = string.length;
         this.sz = 1;                    // work around LDC bug #1286
@@ -2552,7 +2573,7 @@ extern (C++) final class StringExp : Expression
 
     extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope
     {
-        super(loc, EXP.string_, __traits(classInstanceSize, StringExp));
+        super(loc, EXP.string_);
         this.string = cast(char*)string.ptr; // note that this.string should be const
         this.len = len;
         this.sz = sz;
@@ -2750,7 +2771,7 @@ extern (C++) final class StringExp : Expression
         if (sz != 1)
         {
             // Convert to UTF-8 string
-            committed = 0;
+            committed = false;
             Expression e = castTo(sc, Type.tchar.arrayOf());
             e = e.optimize(WANTvalue);
             auto se = e.isStringExp();
@@ -2939,7 +2960,7 @@ extern (C++) final class TupleExp : Expression
 
     extern (D) this(const ref Loc loc, Expression e0, Expressions* exps)
     {
-        super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
+        super(loc, EXP.tuple);
         //printf("TupleExp(this = %p)\n", this);
         this.e0 = e0;
         this.exps = exps;
@@ -2947,14 +2968,14 @@ extern (C++) final class TupleExp : Expression
 
     extern (D) this(const ref Loc loc, Expressions* exps)
     {
-        super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
+        super(loc, EXP.tuple);
         //printf("TupleExp(this = %p)\n", this);
         this.exps = exps;
     }
 
     extern (D) this(const ref Loc loc, TupleDeclaration tup)
     {
-        super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
+        super(loc, EXP.tuple);
         this.exps = new Expressions();
 
         this.exps.reserve(tup.objects.length);
@@ -3031,6 +3052,9 @@ extern (C++) final class TupleExp : Expression
  */
 extern (C++) final class ArrayLiteralExp : Expression
 {
+    OwnedBy ownedByCtfe = OwnedBy.code;
+    bool onstack = false;
+
     /** If !is null, elements[] can be sparse and basis is used for the
      * "default" element value. In other words, non-null elements[i] overrides
      * this 'basis' value.
@@ -3038,19 +3062,17 @@ extern (C++) final class ArrayLiteralExp : Expression
     Expression basis;
 
     Expressions* elements;
-    OwnedBy ownedByCtfe = OwnedBy.code;
-    bool onstack = false;
 
     extern (D) this(const ref Loc loc, Type type, Expressions* elements)
     {
-        super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+        super(loc, EXP.arrayLiteral);
         this.type = type;
         this.elements = elements;
     }
 
     extern (D) this(const ref Loc loc, Type type, Expression e)
     {
-        super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+        super(loc, EXP.arrayLiteral);
         this.type = type;
         elements = new Expressions();
         elements.push(e);
@@ -3058,7 +3080,7 @@ extern (C++) final class ArrayLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements)
     {
-        super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+        super(loc, EXP.arrayLiteral);
         this.type = type;
         this.basis = basis;
         this.elements = elements;
@@ -3196,14 +3218,14 @@ extern (C++) final class ArrayLiteralExp : Expression
  */
 extern (C++) final class AssocArrayLiteralExp : Expression
 {
+    OwnedBy ownedByCtfe = OwnedBy.code;
+
     Expressions* keys;
     Expressions* values;
 
-    OwnedBy ownedByCtfe = OwnedBy.code;
-
     extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values)
     {
-        super(loc, EXP.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp));
+        super(loc, EXP.assocArrayLiteral);
         assert(keys.length == values.length);
         this.keys = keys;
         this.values = values;
@@ -3271,7 +3293,15 @@ extern (C++) final class StructLiteralExp : Expression
     Expressions* elements;  /// parallels sd.fields[] with null entries for fields to skip
     Type stype;             /// final type of result (can be different from sd's type)
 
-    Symbol* sym;            /// back end symbol to initialize with literal
+    // `inlineCopy` is only used temporarily in the `inline.d` pass,
+    // while `sym` is only used in `e2ir/s2ir/tocsym` which comes after
+    union
+    {
+        Symbol* sym;            /// back end symbol to initialize with literal
+
+        /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
+        StructLiteralExp inlinecopy;
+    }
 
     /** pointer to the origin instance of the expression.
      * once a new expression is created, origin is set to 'this'.
@@ -3280,15 +3310,13 @@ extern (C++) final class StructLiteralExp : Expression
      */
     StructLiteralExp origin;
 
-    /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
-    StructLiteralExp inlinecopy;
 
     /** anytime when recursive function is calling, 'stageflags' marks with bit flag of
      * current stage and unmarks before return from this function.
      * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
      * (with infinite recursion) of this expression.
      */
-    int stageflags;
+    ubyte stageflags;
 
     bool useStaticInit;     /// if this is true, use the StructDeclaration's init symbol
     bool isOriginal = false; /// used when moving instances to indicate `this is this.origin`
@@ -3296,7 +3324,7 @@ extern (C++) final class StructLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null)
     {
-        super(loc, EXP.structLiteral, __traits(classInstanceSize, StructLiteralExp));
+        super(loc, EXP.structLiteral);
         this.sd = sd;
         if (!elements)
             elements = new Expressions();
@@ -3475,7 +3503,7 @@ extern (C++) final class CompoundLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, Type type_name, Initializer initializer)
     {
-        super(loc, EXP.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp));
+        super(loc, EXP.compoundLiteral);
         super.type = type_name;
         this.initializer = initializer;
         //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars());
@@ -3494,7 +3522,7 @@ extern (C++) final class TypeExp : Expression
 {
     extern (D) this(const ref Loc loc, Type type)
     {
-        super(loc, EXP.type, __traits(classInstanceSize, TypeExp));
+        super(loc, EXP.type);
         //printf("TypeExp::TypeExp(%s)\n", type.toChars());
         this.type = type;
     }
@@ -3536,7 +3564,7 @@ extern (C++) final class ScopeExp : Expression
 
     extern (D) this(const ref Loc loc, ScopeDsymbol sds)
     {
-        super(loc, EXP.scope_, __traits(classInstanceSize, ScopeExp));
+        super(loc, EXP.scope_);
         //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars());
         //static int count; if (++count == 38) *(char*)0=0;
         this.sds = sds;
@@ -3591,7 +3619,7 @@ extern (C++) final class TemplateExp : Expression
 
     extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null)
     {
-        super(loc, EXP.template_, __traits(classInstanceSize, TemplateExp));
+        super(loc, EXP.template_);
         //printf("TemplateExp(): %s\n", td.toChars());
         this.td = td;
         this.fd = fd;
@@ -3652,7 +3680,7 @@ extern (C++) final class NewExp : Expression
 
     extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null)
     {
-        super(loc, EXP.new_, __traits(classInstanceSize, NewExp));
+        super(loc, EXP.new_);
         this.thisexp = thisexp;
         this.newtype = newtype;
         this.arguments = arguments;
@@ -3690,7 +3718,7 @@ extern (C++) final class NewAnonClassExp : Expression
 
     extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments)
     {
-        super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp));
+        super(loc, EXP.newAnonymousClass);
         this.thisexp = thisexp;
         this.cd = cd;
         this.arguments = arguments;
@@ -3715,9 +3743,9 @@ extern (C++) class SymbolExp : Expression
     Dsymbol originalScope; // original scope before inlining
     bool hasOverloads;
 
-    extern (D) this(const ref Loc loc, EXP op, int size, Declaration var, bool hasOverloads)
+    extern (D) this(const ref Loc loc, EXP op, Declaration var, bool hasOverloads)
     {
-        super(loc, op, size);
+        super(loc, op);
         assert(var);
         this.var = var;
         this.hasOverloads = hasOverloads;
@@ -3746,7 +3774,7 @@ extern (C++) final class SymOffExp : SymbolExp
                 .error(loc, "need `this` for address of `%s`", v.toChars());
             hasOverloads = false;
         }
-        super(loc, EXP.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads);
+        super(loc, EXP.symbolOffset, var, hasOverloads);
         this.offset = offset;
     }
 
@@ -3772,7 +3800,7 @@ extern (C++) final class VarExp : SymbolExp
         if (var.isVarDeclaration())
             hasOverloads = false;
 
-        super(loc, EXP.variable, __traits(classInstanceSize, VarExp), var, hasOverloads);
+        super(loc, EXP.variable, var, hasOverloads);
         //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
         //if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
         this.type = var.type;
@@ -3856,7 +3884,7 @@ extern (C++) final class OverExp : Expression
 
     extern (D) this(const ref Loc loc, OverloadSet s)
     {
-        super(loc, EXP.overloadSet, __traits(classInstanceSize, OverExp));
+        super(loc, EXP.overloadSet);
         //printf("OverExp(this = %p, '%s')\n", this, var.toChars());
         vars = s;
         type = Type.tvoid;
@@ -3890,7 +3918,7 @@ extern (C++) final class FuncExp : Expression
 
     extern (D) this(const ref Loc loc, Dsymbol s)
     {
-        super(loc, EXP.function_, __traits(classInstanceSize, FuncExp));
+        super(loc, EXP.function_);
         this.td = s.isTemplateDeclaration();
         this.fd = s.isFuncLiteralDeclaration();
         if (td)
@@ -4189,7 +4217,7 @@ extern (C++) final class DeclarationExp : Expression
 
     extern (D) this(const ref Loc loc, Dsymbol declaration)
     {
-        super(loc, EXP.declaration, __traits(classInstanceSize, DeclarationExp));
+        super(loc, EXP.declaration);
         this.declaration = declaration;
     }
 
@@ -4222,7 +4250,7 @@ extern (C++) final class TypeidExp : Expression
 
     extern (D) this(const ref Loc loc, RootObject o)
     {
-        super(loc, EXP.typeid_, __traits(classInstanceSize, TypeidExp));
+        super(loc, EXP.typeid_);
         this.obj = o;
     }
 
@@ -4247,7 +4275,7 @@ extern (C++) final class TraitsExp : Expression
 
     extern (D) this(const ref Loc loc, Identifier ident, Objects* args)
     {
-        super(loc, EXP.traits, __traits(classInstanceSize, TraitsExp));
+        super(loc, EXP.traits);
         this.ident = ident;
         this.args = args;
     }
@@ -4272,7 +4300,7 @@ extern (C++) final class HaltExp : Expression
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, EXP.halt, __traits(classInstanceSize, HaltExp));
+        super(loc, EXP.halt);
     }
 
     override void accept(Visitor v)
@@ -4296,7 +4324,7 @@ extern (C++) final class IsExp : Expression
 
     extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope
     {
-        super(loc, EXP.is_, __traits(classInstanceSize, IsExp));
+        super(loc, EXP.is_);
         this.targ = targ;
         this.id = id;
         this.tok = tok;
@@ -4332,11 +4360,10 @@ extern (C++) final class IsExp : Expression
 extern (C++) abstract class UnaExp : Expression
 {
     Expression e1;
-    Type att1;      // Save alias this type to detect recursion
 
-    extern (D) this(const ref Loc loc, EXP op, int size, Expression e1) scope
+    extern (D) this(const ref Loc loc, EXP op, Expression e1) scope
     {
-        super(loc, op, size);
+        super(loc, op);
         this.e1 = e1;
     }
 
@@ -4407,9 +4434,9 @@ extern (C++) abstract class BinExp : Expression
     Type att1;      // Save alias this type to detect recursion
     Type att2;      // Save alias this type to detect recursion
 
-    extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope
+    extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope
     {
-        super(loc, op, size);
+        super(loc, op);
         this.e1 = e1;
         this.e2 = e2;
     }
@@ -4698,9 +4725,9 @@ extern (C++) abstract class BinExp : Expression
  */
 extern (C++) class BinAssignExp : BinExp
 {
-    extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope
+    extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope
     {
-        super(loc, op, size, e1, e2);
+        super(loc, op, e1, e2);
     }
 
     override final bool isLvalue()
@@ -4737,7 +4764,7 @@ extern (C++) final class MixinExp : Expression
 
     extern (D) this(const ref Loc loc, Expressions* exps)
     {
-        super(loc, EXP.mixin_, __traits(classInstanceSize, MixinExp));
+        super(loc, EXP.mixin_);
         this.exps = exps;
     }
 
@@ -4785,7 +4812,7 @@ extern (C++) final class ImportExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.import_, __traits(classInstanceSize, ImportExp), e);
+        super(loc, EXP.import_, e);
     }
 
     override void accept(Visitor v)
@@ -4805,7 +4832,7 @@ extern (C++) final class AssertExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Expression msg = null)
     {
-        super(loc, EXP.assert_, __traits(classInstanceSize, AssertExp), e);
+        super(loc, EXP.assert_, e);
         this.msg = msg;
     }
 
@@ -4830,7 +4857,7 @@ extern (C++) final class ThrowExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.throw_, __traits(classInstanceSize, ThrowExp), e);
+        super(loc, EXP.throw_, e);
         this.type = Type.tnoreturn;
     }
 
@@ -4856,7 +4883,7 @@ extern (C++) final class DotIdExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Identifier ident)
     {
-        super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e);
+        super(loc, EXP.dotIdentifier, e);
         this.ident = ident;
     }
 
@@ -4880,7 +4907,7 @@ extern (C++) final class DotTemplateExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td)
     {
-        super(loc, EXP.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e);
+        super(loc, EXP.dotTemplateDeclaration, e);
         this.td = td;
     }
 
@@ -4914,7 +4941,7 @@ extern (C++) final class DotVarExp : UnaExp
         if (var.isVarDeclaration())
             hasOverloads = false;
 
-        super(loc, EXP.dotVariable, __traits(classInstanceSize, DotVarExp), e);
+        super(loc, EXP.dotVariable, e);
         //printf("DotVarExp()\n");
         this.var = var;
         this.hasOverloads = hasOverloads;
@@ -4995,14 +5022,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs)
     {
-        super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
+        super(loc, EXP.dotTemplateInstance, e);
         //printf("DotTemplateInstanceExp()\n");
         this.ti = new TemplateInstance(loc, name, tiargs);
     }
 
     extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti)
     {
-        super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
+        super(loc, EXP.dotTemplateInstance, e);
         this.ti = ti;
     }
 
@@ -5095,7 +5122,7 @@ extern (C++) final class DelegateExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null)
     {
-        super(loc, EXP.delegate_, __traits(classInstanceSize, DelegateExp), e);
+        super(loc, EXP.delegate_, e);
         this.func = f;
         this.hasOverloads = hasOverloads;
         this.vthis2 = vthis2;
@@ -5115,7 +5142,7 @@ extern (C++) final class DotTypeExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Dsymbol s)
     {
-        super(loc, EXP.dotType, __traits(classInstanceSize, DotTypeExp), e);
+        super(loc, EXP.dotType, e);
         this.sym = s;
     }
 
@@ -5169,19 +5196,19 @@ extern (C++) final class CallExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null)
     {
-        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, e);
         this.arguments = exps;
         this.names = names;
     }
 
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, e);
     }
 
     extern (D) this(const ref Loc loc, Expression e, Expression earg1)
     {
-        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, e);
         this.arguments = new Expressions();
         if (earg1)
             this.arguments.push(earg1);
@@ -5189,7 +5216,7 @@ extern (C++) final class CallExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2)
     {
-        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, e);
         auto arguments = new Expressions(2);
         (*arguments)[0] = earg1;
         (*arguments)[1] = earg2;
@@ -5345,7 +5372,7 @@ extern (C++) final class AddrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.address, __traits(classInstanceSize, AddrExp), e);
+        super(loc, EXP.address, e);
     }
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
@@ -5367,14 +5394,14 @@ extern (C++) final class PtrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e);
+        super(loc, EXP.star, e);
         //if (e.type)
         //  type = ((TypePointer *)e.type).next;
     }
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
     {
-        super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e);
+        super(loc, EXP.star, e);
         type = t;
     }
 
@@ -5420,7 +5447,7 @@ extern (C++) final class NegExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.negate, __traits(classInstanceSize, NegExp), e);
+        super(loc, EXP.negate, e);
     }
 
     override void accept(Visitor v)
@@ -5436,7 +5463,7 @@ extern (C++) final class UAddExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e) scope
     {
-        super(loc, EXP.uadd, __traits(classInstanceSize, UAddExp), e);
+        super(loc, EXP.uadd, e);
     }
 
     override void accept(Visitor v)
@@ -5452,7 +5479,7 @@ extern (C++) final class ComExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.tilde, __traits(classInstanceSize, ComExp), e);
+        super(loc, EXP.tilde, e);
     }
 
     override void accept(Visitor v)
@@ -5468,7 +5495,7 @@ extern (C++) final class NotExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, EXP.not, __traits(classInstanceSize, NotExp), e);
+        super(loc, EXP.not, e);
     }
 
     override void accept(Visitor v)
@@ -5488,7 +5515,7 @@ extern (C++) final class DeleteExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, bool isRAII)
     {
-        super(loc, EXP.delete_, __traits(classInstanceSize, DeleteExp), e);
+        super(loc, EXP.delete_, e);
         this.isRAII = isRAII;
     }
 
@@ -5512,7 +5539,7 @@ extern (C++) final class CastExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
     {
-        super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e);
+        super(loc, EXP.cast_, e);
         this.to = t;
     }
 
@@ -5520,7 +5547,7 @@ extern (C++) final class CastExp : UnaExp
      */
     extern (D) this(const ref Loc loc, Expression e, ubyte mod)
     {
-        super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e);
+        super(loc, EXP.cast_, e);
         this.mod = mod;
     }
 
@@ -5574,7 +5601,7 @@ extern (C++) final class VectorExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
     {
-        super(loc, EXP.vector, __traits(classInstanceSize, VectorExp), e);
+        super(loc, EXP.vector, e);
         assert(t.ty == Tvector);
         to = cast(TypeVector)t;
     }
@@ -5610,7 +5637,7 @@ extern (C++) final class VectorArrayExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, EXP.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1);
+        super(loc, EXP.vectorArray, e1);
     }
 
     override bool isLvalue()
@@ -5641,21 +5668,27 @@ extern (C++) final class SliceExp : UnaExp
     Expression lwr;             // null if implicit [length - 1]
 
     VarDeclaration lengthVar;
-    bool upperIsInBounds;       // true if upr <= e1.length
-    bool lowerIsLessThanUpper;  // true if lwr <= upr
-    bool arrayop;               // an array operation, rather than a slice
+
+    private extern(D) static struct BitFields
+    {
+        bool upperIsInBounds;       // true if upr <= e1.length
+        bool lowerIsLessThanUpper;  // true if lwr <= upr
+        bool arrayop;               // an array operation, rather than a slice
+    }
+    import dmd.common.bitfields : generateBitFields;
+    mixin(generateBitFields!(BitFields, ubyte));
 
     /************************************************************/
     extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie)
     {
-        super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1);
+        super(loc, EXP.slice, e1);
         this.upr = ie ? ie.upr : null;
         this.lwr = ie ? ie.lwr : null;
     }
 
     extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr)
     {
-        super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1);
+        super(loc, EXP.slice, e1);
         this.upr = upr;
         this.lwr = lwr;
     }
@@ -5705,7 +5738,7 @@ extern (C++) final class ArrayLengthExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, EXP.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1);
+        super(loc, EXP.arrayLength, e1);
     }
 
     override void accept(Visitor v)
@@ -5728,7 +5761,7 @@ extern (C++) final class ArrayExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expression index = null)
     {
-        super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1);
+        super(loc, EXP.array, e1);
         arguments = new Expressions();
         if (index)
             arguments.push(index);
@@ -5736,7 +5769,7 @@ extern (C++) final class ArrayExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expressions* args)
     {
-        super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1);
+        super(loc, EXP.array, e1);
         arguments = args;
     }
 
@@ -5773,7 +5806,7 @@ extern (C++) final class DotExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.dot, __traits(classInstanceSize, DotExp), e1, e2);
+        super(loc, EXP.dot, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -5799,7 +5832,7 @@ extern (C++) final class CommaExp : BinExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true)
     {
-        super(loc, EXP.comma, __traits(classInstanceSize, CommaExp), e1, e2);
+        super(loc, EXP.comma, e1, e2);
         allowCommaExp = isGenerated = generated;
     }
 
@@ -5868,7 +5901,7 @@ extern (C++) final class IntervalExp : Expression
 
     extern (D) this(const ref Loc loc, Expression lwr, Expression upr)
     {
-        super(loc, EXP.interval, __traits(classInstanceSize, IntervalExp));
+        super(loc, EXP.interval);
         this.lwr = lwr;
         this.upr = upr;
     }
@@ -5893,7 +5926,7 @@ extern (C++) final class DelegatePtrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, EXP.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1);
+        super(loc, EXP.delegatePointer, e1);
     }
 
     override bool isLvalue()
@@ -5931,7 +5964,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, EXP.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1);
+        super(loc, EXP.delegateFunctionPointer, e1);
     }
 
     override bool isLvalue()
@@ -5971,13 +6004,13 @@ extern (C++) final class IndexExp : BinExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2);
+        super(loc, EXP.index, e1, e2);
         //printf("IndexExp::IndexExp('%s')\n", toChars());
     }
 
     extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool indexIsInBounds)
     {
-        super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2);
+        super(loc, EXP.index, e1, e2);
         this.indexIsInBounds = indexIsInBounds;
         //printf("IndexExp::IndexExp('%s')\n", toChars());
     }
@@ -6054,7 +6087,7 @@ extern (C++) final class PostExp : BinExp
 {
     extern (D) this(EXP op, const ref Loc loc, Expression e)
     {
-        super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1);
+        super(loc, op, e, IntegerExp.literal!1);
         assert(op == EXP.minusMinus || op == EXP.plusPlus);
     }
 
@@ -6071,7 +6104,7 @@ extern (C++) final class PreExp : UnaExp
 {
     extern (D) this(EXP op, const ref Loc loc, Expression e)
     {
-        super(loc, op, __traits(classInstanceSize, PreExp), e);
+        super(loc, op, e);
         assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus);
     }
 
@@ -6101,12 +6134,12 @@ extern (C++) class AssignExp : BinExp
     /* op can be EXP.assign, EXP.construct, or EXP.blit */
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.assign, __traits(classInstanceSize, AssignExp), e1, e2);
+        super(loc, EXP.assign, e1, e2);
     }
 
     this(const ref Loc loc, EXP tok, Expression e1, Expression e2)
     {
-        super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2);
+        super(loc, tok, e1, e2);
     }
 
     override final bool isLvalue()
@@ -6140,6 +6173,32 @@ extern (C++) class AssignExp : BinExp
     }
 }
 
+/***********************************************************
+ * When an assignment expression is lowered to a druntime call
+ * this class is used to store the lowering.
+ * It essentially behaves the same as an AssignExp, but it is
+ * used to not waste space for other AssignExp that are not
+ * lowered to anything.
+ */
+extern (C++) final class LoweredAssignExp : AssignExp
+{
+    Expression lowering;
+    extern (D) this(AssignExp exp, Expression lowering)
+    {
+        super(exp.loc, EXP.loweredAssignExp, exp.e1, exp.e2);
+        this.lowering = lowering;
+    }
+
+    override const(char)* toChars() const
+    {
+        return lowering.toChars();
+    }
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
+
 /***********************************************************
  */
 extern (C++) final class ConstructExp : AssignExp
@@ -6204,7 +6263,7 @@ extern (C++) final class AddAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2);
+        super(loc, EXP.addAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6220,7 +6279,7 @@ extern (C++) final class MinAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2);
+        super(loc, EXP.minAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6236,7 +6295,7 @@ extern (C++) final class MulAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2);
+        super(loc, EXP.mulAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6252,7 +6311,7 @@ extern (C++) final class DivAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2);
+        super(loc, EXP.divAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6268,7 +6327,7 @@ extern (C++) final class ModAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2);
+        super(loc, EXP.modAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6284,7 +6343,7 @@ extern (C++) final class AndAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2);
+        super(loc, EXP.andAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6300,7 +6359,7 @@ extern (C++) final class OrAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2);
+        super(loc, EXP.orAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6316,7 +6375,7 @@ extern (C++) final class XorAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2);
+        super(loc, EXP.xorAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6332,7 +6391,7 @@ extern (C++) final class PowAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2);
+        super(loc, EXP.powAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6348,7 +6407,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2);
+        super(loc, EXP.leftShiftAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6364,7 +6423,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2);
+        super(loc, EXP.rightShiftAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6380,7 +6439,7 @@ extern (C++) final class UshrAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2);
+        super(loc, EXP.unsignedRightShiftAssign, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6405,12 +6464,12 @@ extern (C++) class CatAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2);
+        super(loc, EXP.concatenateAssign, e1, e2);
     }
 
     extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2)
     {
-        super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2);
+        super(loc, tok, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6462,7 +6521,7 @@ extern (C++) final class AddExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.add, __traits(classInstanceSize, AddExp), e1, e2);
+        super(loc, EXP.add, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6480,7 +6539,7 @@ extern (C++) final class MinExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.min, __traits(classInstanceSize, MinExp), e1, e2);
+        super(loc, EXP.min, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6496,9 +6555,11 @@ extern (C++) final class MinExp : BinExp
  */
 extern (C++) final class CatExp : BinExp
 {
+    Expression lowering;  // call to druntime hook `_d_arraycatnTX`
+
     extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope
     {
-        super(loc, EXP.concatenate, __traits(classInstanceSize, CatExp), e1, e2);
+        super(loc, EXP.concatenate, e1, e2);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -6523,7 +6584,7 @@ extern (C++) final class MulExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.mul, __traits(classInstanceSize, MulExp), e1, e2);
+        super(loc, EXP.mul, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6541,7 +6602,7 @@ extern (C++) final class DivExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.div, __traits(classInstanceSize, DivExp), e1, e2);
+        super(loc, EXP.div, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6559,7 +6620,7 @@ extern (C++) final class ModExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.mod, __traits(classInstanceSize, ModExp), e1, e2);
+        super(loc, EXP.mod, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6577,7 +6638,7 @@ extern (C++) final class PowExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.pow, __traits(classInstanceSize, PowExp), e1, e2);
+        super(loc, EXP.pow, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6595,7 +6656,7 @@ extern (C++) final class ShlExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.leftShift, __traits(classInstanceSize, ShlExp), e1, e2);
+        super(loc, EXP.leftShift, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6613,7 +6674,7 @@ extern (C++) final class ShrExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.rightShift, __traits(classInstanceSize, ShrExp), e1, e2);
+        super(loc, EXP.rightShift, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6631,7 +6692,7 @@ extern (C++) final class UshrExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2);
+        super(loc, EXP.unsignedRightShift, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6649,7 +6710,7 @@ extern (C++) final class AndExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.and, __traits(classInstanceSize, AndExp), e1, e2);
+        super(loc, EXP.and, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6667,7 +6728,7 @@ extern (C++) final class OrExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.or, __traits(classInstanceSize, OrExp), e1, e2);
+        super(loc, EXP.or, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6685,7 +6746,7 @@ extern (C++) final class XorExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.xor, __traits(classInstanceSize, XorExp), e1, e2);
+        super(loc, EXP.xor, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6704,7 +6765,7 @@ extern (C++) final class LogicalExp : BinExp
 {
     extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2)
     {
-        super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2);
+        super(loc, op, e1, e2);
         assert(op == EXP.andAnd || op == EXP.orOr);
     }
 
@@ -6726,7 +6787,7 @@ extern (C++) final class CmpExp : BinExp
 {
     extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2);
+        super(loc, op, e1, e2);
         assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual);
     }
 
@@ -6747,7 +6808,7 @@ extern (C++) final class InExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.in_, __traits(classInstanceSize, InExp), e1, e2);
+        super(loc, EXP.in_, e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6765,7 +6826,7 @@ extern (C++) final class RemoveExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, EXP.remove, __traits(classInstanceSize, RemoveExp), e1, e2);
+        super(loc, EXP.remove, e1, e2);
         type = Type.tbool;
     }
 
@@ -6786,7 +6847,7 @@ extern (C++) final class EqualExp : BinExp
 {
     extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2);
+        super(loc, op, e1, e2);
         assert(op == EXP.equal || op == EXP.notEqual);
     }
 
@@ -6807,7 +6868,7 @@ extern (C++) final class IdentityExp : BinExp
 {
     extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2);
+        super(loc, op, e1, e2);
         assert(op == EXP.identity || op == EXP.notIdentity);
     }
 
@@ -6828,7 +6889,7 @@ extern (C++) final class CondExp : BinExp
 
     extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2) scope
     {
-        super(loc, EXP.question, __traits(classInstanceSize, CondExp), e1, e2);
+        super(loc, EXP.question, e1, e2);
         this.econd = econd;
     }
 
@@ -6966,9 +7027,9 @@ bool isDefaultInitOp(EXP op) pure nothrow @safe @nogc
  */
 extern (C++) class DefaultInitExp : Expression
 {
-    extern (D) this(const ref Loc loc, EXP op, int size)
+    extern (D) this(const ref Loc loc, EXP op)
     {
-        super(loc, op, size);
+        super(loc, op);
     }
 
     override void accept(Visitor v)
@@ -6984,7 +7045,7 @@ extern (C++) final class FileInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc, EXP tok)
     {
-        super(loc, tok, __traits(classInstanceSize, FileInitExp));
+        super(loc, tok);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -7015,7 +7076,7 @@ extern (C++) final class LineInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, EXP.line, __traits(classInstanceSize, LineInitExp));
+        super(loc, EXP.line);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -7038,7 +7099,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, EXP.moduleString, __traits(classInstanceSize, ModuleInitExp));
+        super(loc, EXP.moduleString);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -7063,7 +7124,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, EXP.functionString, __traits(classInstanceSize, FuncInitExp));
+        super(loc, EXP.functionString);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -7094,7 +7155,7 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, EXP.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp));
+        super(loc, EXP.prettyFunction);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -7139,8 +7200,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression
 
     extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration)
     {
-        super(loc, EXP.objcClassReference,
-            __traits(classInstanceSize, ObjcClassReferenceExp));
+        super(loc, EXP.objcClassReference);
         this.classDeclaration = classDeclaration;
         type = objc.getRuntimeMetaclass(classDeclaration).getType();
     }
@@ -7163,7 +7223,7 @@ extern (C++) final class GenericExp : Expression
 
     extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps)
     {
-        super(loc, EXP._Generic, __traits(classInstanceSize, GenericExp));
+        super(loc, EXP._Generic);
         this.cntlExp = cntlExp;
         this.types = types;
         this.exps = exps;
@@ -7398,3 +7458,135 @@ private enum EbinaryAssign =
         EXP.leftShiftAssign, EXP.rightShiftAssign, EXP.unsignedRightShiftAssign,
         EXP.concatenateAssign, EXP.concatenateElemAssign, EXP.concatenateDcharAssign,
     ];
+
+/// Given a member of the EXP enum, get the class instance size of the corresponding Expression class.
+/// Needed because the classes are `extern(C++)`
+private immutable ubyte[EXP.max+1] expSize = [
+    EXP.reserved: 0,
+    EXP.negate: __traits(classInstanceSize, NegExp),
+    EXP.cast_: __traits(classInstanceSize, CastExp),
+    EXP.null_: __traits(classInstanceSize, NullExp),
+    EXP.assert_: __traits(classInstanceSize, AssertExp),
+    EXP.array: __traits(classInstanceSize, ArrayExp),
+    EXP.call: __traits(classInstanceSize, CallExp),
+    EXP.address: __traits(classInstanceSize, AddrExp),
+    EXP.type: __traits(classInstanceSize, TypeExp),
+    EXP.throw_: __traits(classInstanceSize, ThrowExp),
+    EXP.new_: __traits(classInstanceSize, NewExp),
+    EXP.delete_: __traits(classInstanceSize, DeleteExp),
+    EXP.star: __traits(classInstanceSize, PtrExp),
+    EXP.symbolOffset: __traits(classInstanceSize, SymOffExp),
+    EXP.variable: __traits(classInstanceSize, VarExp),
+    EXP.dotVariable: __traits(classInstanceSize, DotVarExp),
+    EXP.dotIdentifier: __traits(classInstanceSize, DotIdExp),
+    EXP.dotTemplateInstance: __traits(classInstanceSize, DotTemplateInstanceExp),
+    EXP.dotType: __traits(classInstanceSize, DotTypeExp),
+    EXP.slice: __traits(classInstanceSize, SliceExp),
+    EXP.arrayLength: __traits(classInstanceSize, ArrayLengthExp),
+    EXP.dollar: __traits(classInstanceSize, DollarExp),
+    EXP.template_: __traits(classInstanceSize, TemplateExp),
+    EXP.dotTemplateDeclaration: __traits(classInstanceSize, DotTemplateExp),
+    EXP.declaration: __traits(classInstanceSize, DeclarationExp),
+    EXP.dSymbol: __traits(classInstanceSize, DsymbolExp),
+    EXP.typeid_: __traits(classInstanceSize, TypeidExp),
+    EXP.uadd: __traits(classInstanceSize, UAddExp),
+    EXP.remove: __traits(classInstanceSize, RemoveExp),
+    EXP.newAnonymousClass: __traits(classInstanceSize, NewAnonClassExp),
+    EXP.arrayLiteral: __traits(classInstanceSize, ArrayLiteralExp),
+    EXP.assocArrayLiteral: __traits(classInstanceSize, AssocArrayLiteralExp),
+    EXP.structLiteral: __traits(classInstanceSize, StructLiteralExp),
+    EXP.classReference: __traits(classInstanceSize, ClassReferenceExp),
+    EXP.thrownException: __traits(classInstanceSize, ThrownExceptionExp),
+    EXP.delegatePointer: __traits(classInstanceSize, DelegatePtrExp),
+    EXP.delegateFunctionPointer: __traits(classInstanceSize, DelegateFuncptrExp),
+    EXP.lessThan: __traits(classInstanceSize, CmpExp),
+    EXP.greaterThan: __traits(classInstanceSize, CmpExp),
+    EXP.lessOrEqual: __traits(classInstanceSize, CmpExp),
+    EXP.greaterOrEqual: __traits(classInstanceSize, CmpExp),
+    EXP.equal: __traits(classInstanceSize, EqualExp),
+    EXP.notEqual: __traits(classInstanceSize, EqualExp),
+    EXP.identity: __traits(classInstanceSize, IdentityExp),
+    EXP.notIdentity: __traits(classInstanceSize, IdentityExp),
+    EXP.index: __traits(classInstanceSize, IndexExp),
+    EXP.is_: __traits(classInstanceSize, IsExp),
+    EXP.leftShift: __traits(classInstanceSize, ShlExp),
+    EXP.rightShift: __traits(classInstanceSize, ShrExp),
+    EXP.leftShiftAssign: __traits(classInstanceSize, ShlAssignExp),
+    EXP.rightShiftAssign: __traits(classInstanceSize, ShrAssignExp),
+    EXP.unsignedRightShift: __traits(classInstanceSize, UshrExp),
+    EXP.unsignedRightShiftAssign: __traits(classInstanceSize, UshrAssignExp),
+    EXP.concatenate: __traits(classInstanceSize, CatExp),
+    EXP.concatenateAssign: __traits(classInstanceSize, CatAssignExp),
+    EXP.concatenateElemAssign: __traits(classInstanceSize, CatElemAssignExp),
+    EXP.concatenateDcharAssign: __traits(classInstanceSize, CatDcharAssignExp),
+    EXP.add: __traits(classInstanceSize, AddExp),
+    EXP.min: __traits(classInstanceSize, MinExp),
+    EXP.addAssign: __traits(classInstanceSize, AddAssignExp),
+    EXP.minAssign: __traits(classInstanceSize, MinAssignExp),
+    EXP.mul: __traits(classInstanceSize, MulExp),
+    EXP.div: __traits(classInstanceSize, DivExp),
+    EXP.mod: __traits(classInstanceSize, ModExp),
+    EXP.mulAssign: __traits(classInstanceSize, MulAssignExp),
+    EXP.divAssign: __traits(classInstanceSize, DivAssignExp),
+    EXP.modAssign: __traits(classInstanceSize, ModAssignExp),
+    EXP.and: __traits(classInstanceSize, AndExp),
+    EXP.or: __traits(classInstanceSize, OrExp),
+    EXP.xor: __traits(classInstanceSize, XorExp),
+    EXP.andAssign: __traits(classInstanceSize, AndAssignExp),
+    EXP.orAssign: __traits(classInstanceSize, OrAssignExp),
+    EXP.xorAssign: __traits(classInstanceSize, XorAssignExp),
+    EXP.assign: __traits(classInstanceSize, AssignExp),
+    EXP.not: __traits(classInstanceSize, NotExp),
+    EXP.tilde: __traits(classInstanceSize, ComExp),
+    EXP.plusPlus: __traits(classInstanceSize, PostExp),
+    EXP.minusMinus: __traits(classInstanceSize, PostExp),
+    EXP.construct: __traits(classInstanceSize, ConstructExp),
+    EXP.blit: __traits(classInstanceSize, BlitExp),
+    EXP.dot: __traits(classInstanceSize, DotExp),
+    EXP.comma: __traits(classInstanceSize, CommaExp),
+    EXP.question: __traits(classInstanceSize, CondExp),
+    EXP.andAnd: __traits(classInstanceSize, LogicalExp),
+    EXP.orOr: __traits(classInstanceSize, LogicalExp),
+    EXP.prePlusPlus: __traits(classInstanceSize, PreExp),
+    EXP.preMinusMinus: __traits(classInstanceSize, PreExp),
+    EXP.identifier: __traits(classInstanceSize, IdentifierExp),
+    EXP.string_: __traits(classInstanceSize, StringExp),
+    EXP.this_: __traits(classInstanceSize, ThisExp),
+    EXP.super_: __traits(classInstanceSize, SuperExp),
+    EXP.halt: __traits(classInstanceSize, HaltExp),
+    EXP.tuple: __traits(classInstanceSize, TupleExp),
+    EXP.error: __traits(classInstanceSize, ErrorExp),
+    EXP.void_: __traits(classInstanceSize, VoidInitExp),
+    EXP.int64: __traits(classInstanceSize, IntegerExp),
+    EXP.float64: __traits(classInstanceSize, RealExp),
+    EXP.complex80: __traits(classInstanceSize, ComplexExp),
+    EXP.import_: __traits(classInstanceSize, ImportExp),
+    EXP.delegate_: __traits(classInstanceSize, DelegateExp),
+    EXP.function_: __traits(classInstanceSize, FuncExp),
+    EXP.mixin_: __traits(classInstanceSize, MixinExp),
+    EXP.in_: __traits(classInstanceSize, InExp),
+    EXP.break_: __traits(classInstanceSize, CTFEExp),
+    EXP.continue_: __traits(classInstanceSize, CTFEExp),
+    EXP.goto_: __traits(classInstanceSize, CTFEExp),
+    EXP.scope_: __traits(classInstanceSize, ScopeExp),
+    EXP.traits: __traits(classInstanceSize, TraitsExp),
+    EXP.overloadSet: __traits(classInstanceSize, OverExp),
+    EXP.line: __traits(classInstanceSize, LineInitExp),
+    EXP.file: __traits(classInstanceSize, FileInitExp),
+    EXP.fileFullPath: __traits(classInstanceSize, FileInitExp),
+    EXP.moduleString: __traits(classInstanceSize, ModuleInitExp),
+    EXP.functionString: __traits(classInstanceSize, FuncInitExp),
+    EXP.prettyFunction: __traits(classInstanceSize, PrettyFuncInitExp),
+    EXP.pow: __traits(classInstanceSize, PowExp),
+    EXP.powAssign: __traits(classInstanceSize, PowAssignExp),
+    EXP.vector: __traits(classInstanceSize, VectorExp),
+    EXP.voidExpression: __traits(classInstanceSize, CTFEExp),
+    EXP.cantExpression: __traits(classInstanceSize, CTFEExp),
+    EXP.showCtfeContext: __traits(classInstanceSize, CTFEExp),
+    EXP.objcClassReference: __traits(classInstanceSize, ObjcClassReferenceExp),
+    EXP.vectorArray: __traits(classInstanceSize, VectorArrayExp),
+    EXP.compoundLiteral: __traits(classInstanceSize, CompoundLiteralExp),
+    EXP._Generic: __traits(classInstanceSize, GenericExp),
+    EXP.interval: __traits(classInstanceSize, IntervalExp),
+    EXP.loweredAssignExp : __traits(classInstanceSize, LoweredAssignExp),
+];
index 1bc78e7ef2a2c7ffc9f3dabe6444b8bd1a17185a..a4b18b9fe9efb23678066e5c76ff21d7e9b2a651 100644 (file)
@@ -38,6 +38,7 @@ class TemplateDeclaration;
 class ClassDeclaration;
 class OverloadSet;
 class StringExp;
+class LoweredAssignExp;
 struct UnionExp;
 #ifdef IN_GCC
 typedef union tree_node Symbol;
@@ -79,12 +80,12 @@ enum class ModifyFlags
 class Expression : public ASTNode
 {
 public:
-    EXP op;                     // to minimize use of dynamic_cast
-    unsigned char size;         // # of bytes in Expression so we can copy() it
-    d_bool parens;                // if this is a parenthesized expression
     Type *type;                 // !=NULL means that semantic() has been run
     Loc loc;                    // file location
+    EXP op;                     // to minimize use of dynamic_cast
+    d_bool parens;              // if this is a parenthesized expression
 
+    size_t size() const;
     static void _init();
     Expression *copy();
     virtual Expression *syntaxCopy();
@@ -240,6 +241,7 @@ public:
     UnaExp* isUnaExp();
     BinExp* isBinExp();
     BinAssignExp* isBinAssignExp();
+    LoweredAssignExp* isLoweredAssignExp();
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -370,12 +372,12 @@ public:
 class StringExp final : public Expression
 {
 public:
+    utf8_t postfix;      // 'c', 'w', 'd'
+    OwnedBy ownedByCtfe;
     void *string;       // char, wchar, or dchar data
     size_t len;         // number of chars, wchars, or dchars
     unsigned char sz;   // 1: char, 2: wchar, 4: dchar
-    unsigned char committed;    // !=0 if type is committed
-    utf8_t postfix;      // 'c', 'w', 'd'
-    OwnedBy ownedByCtfe;
+    bool committed;     // if type is committed
 
     static StringExp *create(const Loc &loc, const char *s);
     static StringExp *create(const Loc &loc, const void *s, d_size_t len);
@@ -419,10 +421,10 @@ public:
 class ArrayLiteralExp final : public Expression
 {
 public:
-    Expression *basis;
-    Expressions *elements;
     OwnedBy ownedByCtfe;
     d_bool onstack;
+    Expression *basis;
+    Expressions *elements;
 
     static ArrayLiteralExp *create(const Loc &loc, Expressions *elements);
     static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements);
@@ -439,9 +441,9 @@ public:
 class AssocArrayLiteralExp final : public Expression
 {
 public:
+    OwnedBy ownedByCtfe;
     Expressions *keys;
     Expressions *values;
-    OwnedBy ownedByCtfe;
 
     bool equals(const RootObject * const o) const override;
     AssocArrayLiteralExp *syntaxCopy() override;
@@ -457,7 +459,13 @@ public:
     Expressions *elements;      // parallels sd->fields[] with NULL entries for fields to skip
     Type *stype;                // final type of result (can be different from sd's type)
 
-    Symbol *sym;                // back end symbol to initialize with literal
+    union
+    {
+        Symbol *sym;                // back end symbol to initialize with literal
+
+        // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
+        StructLiteralExp *inlinecopy;
+    };
 
     /** pointer to the origin instance of the expression.
      * once a new expression is created, origin is set to 'this'.
@@ -466,15 +474,13 @@ public:
      */
     StructLiteralExp *origin;
 
-    // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
-    StructLiteralExp *inlinecopy;
 
     /** anytime when recursive function is calling, 'stageflags' marks with bit flag of
      * current stage and unmarks before return from this function.
      * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
      * (with infinite recursion) of this expression.
      */
-    int stageflags;
+    uint8_t stageflags;
 
     d_bool useStaticInit;         // if this is true, use the StructDeclaration's init symbol
     d_bool isOriginal;            // used when moving instances to indicate `this is this.origin`
@@ -693,7 +699,6 @@ class UnaExp : public Expression
 {
 public:
     Expression *e1;
-    Type *att1; // Save alias this type to detect recursion
 
     UnaExp *syntaxCopy() override;
     Expression *incompatibleTypes();
@@ -937,9 +942,15 @@ public:
     Expression *upr;            // NULL if implicit 0
     Expression *lwr;            // NULL if implicit [length - 1]
     VarDeclaration *lengthVar;
-    d_bool upperIsInBounds;       // true if upr <= e1.length
-    d_bool lowerIsLessThanUpper;  // true if lwr <= upr
-    d_bool arrayop;               // an array operation, rather than a slice
+
+    bool upperIsInBounds() const; // true if upr <= e1.length
+    bool upperIsInBounds(bool v);
+    bool lowerIsLessThanUpper() const; // true if lwr <= upr
+    bool lowerIsLessThanUpper(bool v);
+    bool arrayop() const; // an array operation, rather than a slice
+    bool arrayop(bool v);
+private:
+    uint8_t bitFields;
 
     SliceExp *syntaxCopy() override;
     bool isLvalue() override;
@@ -1076,6 +1087,15 @@ public:
     void accept(Visitor *v) override { v->visit(this); }
 };
 
+class LoweredAssignExp final : public AssignExp
+{
+public:
+    Expression *lowering;
+
+    const char *toChars() const override;
+    void accept(Visitor *v) override { v->visit(this); }
+};
+
 class BlitExp final : public AssignExp
 {
 public:
@@ -1187,6 +1207,8 @@ public:
 class CatExp final : public BinExp
 {
 public:
+    Expression *lowering;  // call to druntime hook `_d_arraycatnTX`
+
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -1372,7 +1394,7 @@ struct UnionExp
 
     UnionExp(Expression *e)
     {
-        memcpy(this, (void *)e, e->size);
+        memcpy(this, (void *)e, e->size());
     }
 
     /* Extract pointer to Expression
index 45dcb9739f290190952cf86f51211deb739f652b..cf4aac4a8896d73bb880943f1e49526be695cbde 100644 (file)
@@ -65,7 +65,6 @@ import dmd.parse;
 import dmd.printast;
 import dmd.root.array;
 import dmd.root.ctfloat;
-import dmd.root.file;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rootobject;
@@ -635,7 +634,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce)
     }
     else if (auto dti = ce.e1.isDotTemplateInstanceExp())
     {
-        if (Expression ey = dti.dotTemplateSemanticProp(sc, 1))
+        if (Expression ey = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag))
         {
             ce.e1 = ey;
             return null;
@@ -1221,7 +1220,7 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc)
 /***************************************
  * Pull out any properties.
  */
-private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null)
+private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null)
 {
     //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null);
     Loc loc = e1.loc;
@@ -1295,7 +1294,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
             {
                 Expression e = new CallExp(loc, e1);
                 if (e2)
+                {
                     e = new AssignExp(loc, e, e2);
+                    if (saveAtts)
+                    {
+                        (cast(BinExp)e).att1 = saveAtts.att1;
+                        (cast(BinExp)e).att2 = saveAtts.att2;
+                    }
+                }
                 return e.expressionSemantic(sc);
             }
         }
@@ -1413,7 +1419,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
                     }
                     Expression e = new CallExp(loc, e1);
                     if (e2)
+                    {
                         e = new AssignExp(loc, e, e2);
+                        if (saveAtts)
+                        {
+                            (cast(BinExp)e).att1 = saveAtts.att1;
+                            (cast(BinExp)e).att2 = saveAtts.att2;
+                        }
+                    }
                     return e.expressionSemantic(sc);
                 }
             }
@@ -2177,7 +2190,11 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
             }
             // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along
             // as lazy parameters to the next function, but that isn't escaping.
-            else if (!(pStc & STC.lazy_))
+            // The arguments of `_d_arraycatnTX` are already handled in
+            // expressionsem.d, via `checkNewEscape`. Without `-dip1000`, the
+            // check does not return an error, so the lowering of `a ~ b` to
+            // `_d_arraycatnTX(a, b)` still occurs.
+            else if (!(pStc & STC.lazy_) && (!fd ||  fd.ident != Id._d_arraycatnTX))
             {
                 /* Argument value can escape from the called function.
                  * Check arg to see if it matters.
@@ -2208,6 +2225,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                     // allocate the array literal as temporary static array on the stack
                     ale.type = ale.type.nextOf().sarrayOf(ale.elements.length);
                     auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale);
+                    tmp.storage_class |= STC.exptemp;
                     auto declareTmp = new DeclarationExp(ale.loc, tmp);
                     auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp),
                         p.type.substWildTo(MODFlags.mutable));
@@ -2266,7 +2284,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                 default:
                     break;
                 }
-                if (tf.parameterList.varargs == VarArg.variadic)
+                if (tf.parameterList.varargs == VarArg.variadic ||
+                    tf.parameterList.varargs == VarArg.KRvariadic)
                 {
                     const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)";
                     if (arg.type.ty == Tarray)
@@ -2348,30 +2367,18 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
     }
 
     /* Remaining problems:
-     * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
-     *    implemented by calling a function) we'll defer this for now.
-     * 2. value structs (or static arrays of them) that need to be copy constructed
-     * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
+     * 1. value structs (or static arrays of them) that need to be copy constructed
+     * 2. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
      *    function gets called.
-     * 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments.
-     * 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
+     * 3. value structs need to be destructed after the function call for platforms where the caller destroys the arguments.
+     * Those are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
      * up properly. Pushing arguments on the stack then cannot fail.
      */
      {
-        /* TODO: tackle problem 1)
-         */
-        const bool leftToRight = true; // TODO: Any cases that need rightToLeft?
-        if (!leftToRight)
-            assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity
-
-        /* Does Problem (4) apply?
+        /* Does Problem (3) apply?
          */
         const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf);
 
-        const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1);
-        const ptrdiff_t end   = (leftToRight ? cast(ptrdiff_t)nargs : -1);
-        const ptrdiff_t step  = (leftToRight ? 1 : -1);
-
         /* Compute indices of last throwing argument and first arg needing destruction.
          * Used to not set up destructors unless an arg needs destruction on a throw
          * in a later argument.
@@ -2379,7 +2386,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
         ptrdiff_t lastthrow = -1;   // last argument that may throw
         ptrdiff_t firstdtor = -1;   // first argument that needs destruction
         ptrdiff_t lastdtor  = -1;   // last argument that needs destruction
-        for (ptrdiff_t i = start; i != end; i += step)
+        for (ptrdiff_t i = 0; i != nargs; i++)
         {
             Expression arg = (*arguments)[i];
             if (canThrow(arg, sc.func, false))
@@ -2396,12 +2403,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
             }
         }
 
-        /* Do we need 'eprefix' for problems 3 or 4?
+        /* Do we need 'eprefix' for problems 2 or 3?
          */
         const bool needsPrefix = callerDestroysArgs
             ? firstdtor >= 0 // true if any argument needs destruction
             : firstdtor >= 0 && lastthrow >= 0 &&
-              (lastthrow - firstdtor) * step > 0; // last throw after first destruction
+              (lastthrow - firstdtor) > 0; // last throw after first destruction
         const ptrdiff_t lastPrefix = callerDestroysArgs
             ? lastdtor   // up to last argument requiring destruction
             : lastthrow; // up to last potentially throwing argument
@@ -2421,7 +2428,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
             eprefix = ae.expressionSemantic(sc);
         }
 
-        for (ptrdiff_t i = start; i != end; i += step)
+        for (ptrdiff_t i = 0; i != nargs; i++)
         {
             Expression arg = (*arguments)[i];
             //printf("arg[%d]: %s\n", cast(int)i, arg.toChars());
@@ -2437,12 +2444,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
 
             /* Do we have 'eprefix' and aren't past 'lastPrefix' yet?
              * Then declare a temporary variable for this arg and append that declaration
-             * to 'eprefix', which will implicitly take care of potential problem 2) for
+             * to 'eprefix', which will implicitly take care of potential problem 1) for
              * this arg.
              * 'eprefix' will therefore finally contain all args up to and including 'lastPrefix',
              * excluding all lazy parameters.
              */
-            if (needsPrefix && (lastPrefix - i) * step >= 0)
+            if (needsPrefix && (lastPrefix - i) >= 0)
             {
                 const bool needsDtor = !isRef && arg.type.needsDestruction() &&
                                        // Problem 3: last throwing arg doesn't require dtor patching
@@ -2465,7 +2472,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                 }
                 else
                 {
-                    /* Problem 3: Modify the destructor so it only runs if gate==false,
+                    /* Problem 2: Modify the destructor so it only runs if gate==false,
                      * i.e., only if there was a throw while constructing the args
                      */
                     if (!needsDtor)
@@ -2500,7 +2507,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                     arg = arg.expressionSemantic(sc);
                 }
 
-                /* Problem 3: Last throwing arg?
+                /* Problem 2: Last throwing arg?
                  * Then finalize eprefix => (eprefix, gate = true), i.e., disable the
                  * dtors right after constructing the last throwing arg.
                  * From now on, the callee will take care of destructing the args because
@@ -2514,7 +2521,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
             }
             else // not part of 'eprefix'
             {
-                /* Handle problem 2) by calling the copy constructor for value structs
+                /* Handle problem 1) by calling the copy constructor for value structs
                  * (or static arrays of them) if appropriate.
                  */
                 Type tv = arg.type.baseElemOf();
@@ -2671,6 +2678,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     {
         if (!e.type)
             e.type = Type.tfloat64;
+        else if (e.type.isimaginary && sc.flags & SCOPE.Cfile)
+        {
+            /* Convert to core.stdc.config.complex
+             */
+            Type t = getComplexLibraryType(e.loc, sc, e.type.ty);
+            if (t.ty == Terror)
+                return setError();
+
+            Type tf;
+            switch (e.type.ty)
+            {
+                case Timaginary32: tf = Type.tfloat32; break;
+                case Timaginary64: tf = Type.tfloat64; break;
+                case Timaginary80: tf = Type.tfloat80; break;
+                default:
+                    assert(0);
+            }
+
+            /* Construct ts{re : 0.0, im : e}
+             */
+            TypeStruct ts = t.isTypeStruct;
+            Expressions* elements = new Expressions(2);
+            (*elements)[0] = new RealExp(e.loc,    CTFloat.zero, tf);
+            (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf);
+            Expression sle = new StructLiteralExp(e.loc, ts.sym, elements);
+            result = sle.expressionSemantic(sc);
+            return;
+        }
         else
             e.type = e.type.typeSemantic(e.loc, sc);
         result = e;
@@ -2933,7 +2968,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (!s)
                 {
                     e.error("`%s` is not in a class or struct scope", e.toChars());
-                    goto Lerr;
+                    return setError();
                 }
                 ClassDeclaration cd = s.isClassDeclaration();
                 if (cd)
@@ -2952,7 +2987,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
         if (!fd)
-            goto Lerr;
+        {
+            e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars());
+            return setError();
+        }
 
         assert(fd.vthis);
         e.var = fd.vthis;
@@ -2967,11 +3005,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return setError();
 
         result = e;
-        return;
-
-    Lerr:
-        e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars());
-        result = ErrorExp.get();
     }
 
     override void visit(SuperExp e)
@@ -3001,7 +3034,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (!s)
                 {
                     e.error("`%s` is not in a class scope", e.toChars());
-                    goto Lerr;
+                    return setError();
                 }
                 cd = s.isClassDeclaration();
                 if (cd)
@@ -3010,7 +3043,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     if (!cd)
                     {
                         e.error("class `%s` has no `super`", s.toChars());
-                        goto Lerr;
+                        return setError();
                     }
                     e.type = cd.type;
                     result = e;
@@ -3109,7 +3142,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 e.type = Type.tuns32.sarrayOf(e.len + 1);
             else
                 e.type = Type.tdchar.immutableOf().arrayOf();
-            e.committed = 1;
+            e.committed = true;
             break;
 
         case 'w':
@@ -3134,11 +3167,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 e.type = Type.tuns16.sarrayOf(e.len + 1);
             else
                 e.type = Type.twchar.immutableOf().arrayOf();
-            e.committed = 1;
+            e.committed = true;
             break;
 
         case 'c':
-            e.committed = 1;
+            e.committed = true;
             goto default;
 
         default:
@@ -3860,8 +3893,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 result = id.expressionSemantic(sc);
                 return;
             }
-            else if (!exp.onstack && !exp.type.isscope())
+            else if (sc.needsCodegen() && // interpreter doesn't need this lowered
+                     !exp.onstack && !exp.type.isscope()) // these won't use the GC
             {
+                /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)`
+                 * or `_d_newclassTTrace`
+                 */
                 auto hook = global.params.tracegc ? Id._d_newclassTTrace : Id._d_newclassT;
                 if (!verifyHookExist(exp.loc, *sc, hook, "new class"))
                     return setError();
@@ -4533,6 +4570,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
+        Type att = null;
     Lagain:
         //printf("Lagain: %s\n", toChars());
         exp.f = null;
@@ -4743,7 +4781,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 // overload of opCall, therefore it's a call
                 if (exp.e1.op != EXP.type)
                 {
-                    if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type))
+                    if (sd.aliasthis && !isRecursiveAliasThis(att, exp.e1.type))
                     {
                         exp.e1 = resolveAliasThis(sc, exp.e1);
                         goto Lagain;
@@ -4832,10 +4870,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                         return null;
                     if (f)
                     {
-                        /* Error if match in more than one overload set,
+                        /* Match in more than one overload set,
                          * even if one is a 'better' match than the other.
                          */
-                        ScopeDsymbol.multiplyDefined(loc, f, f2);
+                        if (f.isCsymbol() && f2.isCsymbol())
+                        {
+                            /* C has global name space, so just pick one, such as f.
+                             * If f and f2 are not compatible, that's how C rolls.
+                             */
+                        }
+                        else
+                            ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error
                     }
                     else
                         f = f2;
@@ -5203,13 +5248,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_)))
             {
                 bool err = false;
-                if (!tf.purity && sc.func.setImpure())
+                if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1))
                 {
                     exp.error("`pure` %s `%s` cannot call impure %s `%s`",
                         sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
                     err = true;
                 }
-                if (!tf.isnogc && sc.func.setGC())
+                if (!tf.isnogc && sc.func.setGC(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc `%s`", exp.e1))
                 {
                     exp.error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
                         sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
@@ -6166,7 +6211,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         uint errors = global.errors;
         const len = buf.length;
         const str = buf.extractChars()[0 .. len];
-        scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink);
+        const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+        auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut);
+        scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
+        p.transitionIn = global.params.vin;
         p.nextToken();
         //printf("p.loc.linnum = %d\n", p.loc.linnum);
 
@@ -6295,19 +6343,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
             else
             {
-                auto readResult = File.read(resolvedNamez);
-                if (!readResult.success)
-                {
-                    e.error("cannot read file `%s`", resolvedNamez.ptr);
-                    return setError();
-                }
-                else
-                {
-                    // take ownership of buffer (probably leaking)
-                    auto data = readResult.extractSlice();
-                    se = new StringExp(e.loc, data);
-                    global.fileManager.add(fileName, data);
-                }
+                e.error("cannot read file `%s`", resolvedNamez.ptr);
+                return setError();
             }
         }
         result = se.expressionSemantic(sc);
@@ -6321,7 +6358,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("AssertExp::semantic('%s')\n", exp.toChars());
         }
 
-        const generateMsg = !exp.msg && global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on;
+        const generateMsg = !exp.msg &&
+                            sc.needsCodegen() && // let ctfe interpreter handle the error message
+                            global.params.checkAction == CHECKACTION.context &&
+                            global.params.useAssert == CHECKENABLE.on;
         Expression temporariesPrefix;
 
         if (generateMsg)
@@ -6634,7 +6674,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     {
         import dmd.statementsem;
 
-        if (StatementSemanticVisitor.throwSemantic(te.loc, te.e1, sc))
+        if (throwSemantic(te.loc, te.e1, sc))
             result = te;
         else
             setError();
@@ -6927,7 +6967,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
         // Indicate we need to resolve by UFCS.
-        Expression e = exp.dotTemplateSemanticProp(sc, 1);
+        Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag);
         if (!e)
             e = resolveUFCSProperties(sc, exp);
         if (e is exp)
@@ -7824,7 +7864,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
 
-            if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0)
+            if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && sc.needsCodegen())
             {
                 auto tFrom = t1b.nextOf();
                 auto tTo = tob.nextOf();
@@ -8914,6 +8954,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 assert((*ae.arguments)[0].op == EXP.interval);
                 ie = cast(IntervalExp)(*ae.arguments)[0];
             }
+            Type att = null; // first cyclic `alias this` type
             while (true)
             {
                 if (ae.e1.op == EXP.error)
@@ -8988,7 +9029,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
                 // No operator overloading member function found yet, but
                 // there might be an alias this to try.
-                if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+                if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
                 {
                     /* Rewrite (a[arguments] op e2) as:
                      *      a.aliasthis[arguments] op e2
@@ -9016,7 +9057,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
              */
             if (auto dti = e1x.isDotTemplateInstanceExp())
             {
-                Expression e = dti.dotTemplateSemanticProp(sc, 1);
+                Expression e = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag);
                 if (!e)
                 {
                     return setResult(resolveUFCSProperties(sc, e1x, exp.e2));
@@ -9070,7 +9111,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
              * or:
              *      f() = value
              */
-            if (Expression e = resolvePropertiesX(sc, e1x, exp.e2))
+            if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp))
                 return setResult(e);
 
             if (e1x.checkRightThis(sc))
@@ -9764,6 +9805,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 return setResult(res);
             }
 
+            if (!sc.needsCodegen())      // if compile time creature only
+            {
+                exp.type = Type.tsize_t;
+                return setResult(exp);
+            }
+
             // Lower to object._d_arraysetlengthTImpl!(typeof(e1))._d_arraysetlengthT{,Trace}(e1, e2)
             Expression id = new IdentifierExp(ale.loc, Id.empty);
             id = new DotIdExp(ale.loc, id, Id.object);
@@ -9785,10 +9832,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             arguments.push(ale.e1);
             arguments.push(exp.e2);
 
-            Expression ce = new CallExp(ale.loc, id, arguments);
-            auto res = ce.expressionSemantic(sc);
+            Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc);
+            auto res = new LoweredAssignExp(exp, ce);
             // if (global.params.verbose)
             //     message("lowered   %s =>\n          %s", exp.toChars(), res.toChars());
+            res.type = Type.tsize_t;
             return setResult(res);
         }
         else if (auto se = exp.e1.isSliceExp())
@@ -10117,6 +10165,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     }
                 }
             }
+
+            if (!sc.needsCodegen()) // interpreter can handle these
+                return setResult(res);
+
             const lowerToArrayCtor =
                 ( (rhsType.ty == Tarray && !rhs.isArrayLiteralExp) ||
                   (rhsType.ty == Tsarray && rhs.isLvalue) ) &&
@@ -10505,7 +10557,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         result = res;
 
         if ((exp.op == EXP.concatenateAssign || exp.op == EXP.concatenateElemAssign) &&
-            !(sc.flags & (SCOPE.ctfe | SCOPE.compile)))
+            sc.needsCodegen())
         {
             // if aa ordering is triggered, `res` will be a CommaExp
             // and `.e2` will be the rewritten original expression.
@@ -10898,6 +10950,86 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         return;
     }
 
+    /**
+     * If the given expression is a `CatExp`, the function tries to lower it to
+     * `_d_arraycatnTX`.
+     *
+     * Params:
+     *      ee = the `CatExp` to lower
+     * Returns:
+     *      `_d_arraycatnTX(e1, e2, ..., en)` if `ee` is `e1 ~ e2 ~ ... en`
+     *      `ee` otherwise
+     */
+    private Expression lowerToArrayCat(CatExp exp)
+    {
+        // String literals are concatenated by the compiler. No lowering is needed.
+        if ((exp.e1.isStringExp() && (exp.e2.isIntegerExp() || exp.e2.isStringExp())) ||
+            (exp.e2.isStringExp() && (exp.e1.isIntegerExp() || exp.e1.isStringExp())))
+            return exp;
+
+        Identifier hook = global.params.tracegc ? Id._d_arraycatnTXTrace : Id._d_arraycatnTX;
+        if (!verifyHookExist(exp.loc, *sc, hook, "concatenating arrays"))
+        {
+            setError();
+            return result;
+        }
+
+        void handleCatArgument(Expressions *arguments, Expression e)
+        {
+            if (auto ce = e.isCatExp())
+            {
+                Expression lowering = ce.lowering;
+
+                /* Skip `file`, `line`, and `funcname` if the hook of the parent
+                 * `CatExp` is `_d_arraycatnTXTrace`.
+                 */
+                if (auto callExp = isRuntimeHook(lowering, hook))
+                {
+                    if (hook == Id._d_arraycatnTX)
+                        arguments.pushSlice((*callExp.arguments)[]);
+                    else
+                        arguments.pushSlice((*callExp.arguments)[3 .. $]);
+                }
+            }
+            else
+                arguments.push(e);
+        }
+
+        auto arguments = new Expressions();
+        if (global.params.tracegc)
+        {
+            auto funcname = (sc.callsc && sc.callsc.func) ?
+                sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars();
+            arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString()));
+            arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32));
+            arguments.push(new StringExp(exp.loc, funcname.toDString()));
+        }
+
+        handleCatArgument(arguments, exp.e1);
+        handleCatArgument(arguments, exp.e2);
+
+        Expression id = new IdentifierExp(exp.loc, Id.empty);
+        id = new DotIdExp(exp.loc, id, Id.object);
+
+        auto tiargs = new Objects();
+        tiargs.push(exp.type);
+        id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs);
+        id = new CallExp(exp.loc, id, arguments);
+        return id.expressionSemantic(sc);
+    }
+
+    void trySetCatExpLowering(Expression exp)
+    {
+        /* `_d_arraycatnTX` canot be used with `-betterC`, but `CatExp`s may be
+         * used with `-betterC`, but only during CTFE.
+         */
+        if (global.params.betterC)
+            return;
+
+        if (auto ce = exp.isCatExp())
+            ce.lowering = lowerToArrayCat(ce);
+    }
+
     override void visit(CatExp exp)
     {
         // https://dlang.org/spec/expression.html#cat_expressions
@@ -10985,14 +11117,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 exp.e2 = exp.e2.implicitCastTo(sc, tb1next);
                 exp.type = tb1next.arrayOf();
             L2elem:
-                if (tb2.ty == Tarray || tb2.ty == Tsarray)
-                {
-                    // Make e2 into [e2]
-                    exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2);
-                }
-                else if (checkNewEscape(sc, exp.e2, false))
+                if (checkNewEscape(sc, exp.e2, false))
                     return setError();
                 result = exp.optimize(WANTvalue);
+                trySetCatExpLowering(result);
                 return;
             }
         }
@@ -11023,14 +11151,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 exp.e1 = exp.e1.implicitCastTo(sc, tb2next);
                 exp.type = tb2next.arrayOf();
             L1elem:
-                if (tb1.ty == Tarray || tb1.ty == Tsarray)
-                {
-                    // Make e1 into [e1]
-                    exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1);
-                }
-                else if (checkNewEscape(sc, exp.e1, false))
+                if (checkNewEscape(sc, exp.e1, false))
                     return setError();
                 result = exp.optimize(WANTvalue);
+                trySetCatExpLowering(result);
                 return;
             }
         }
@@ -11053,6 +11177,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (Expression ex = typeCombine(exp, sc))
         {
             result = ex;
+            trySetCatExpLowering(result);
             return;
         }
         exp.type = exp.type.toHeadMutable();
@@ -11085,6 +11210,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         result = e;
+        trySetCatExpLowering(result);
     }
 
     override void visit(MulExp exp)
@@ -11895,7 +12021,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 exp.error("array comparison type mismatch, `%s` vs `%s`", t1next.toChars(), t2next.toChars());
                 return setError();
             }
-            if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray))
+
+            if (sc.needsCodegen() &&
+                (t1.ty == Tarray || t1.ty == Tsarray) &&
+                (t2.ty == Tarray || t2.ty == Tsarray))
             {
                 if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays"))
                     return setError();
@@ -12762,7 +12891,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
     if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length)
     {
         // bypass checkPurity
-        return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0);
+        return exp.e1.type.dotExp(sc, exp.e1, exp.ident, cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref));
     }
 
     if (!exp.e1.isDotExp())
@@ -12814,11 +12943,11 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
  * Params:
  *      exp = expression to resolve
  *      sc = context
- *      flag = if 1 then do not emit error messages, just return null
+ *      gag = do not emit error messages, just return `null`
  * Returns:
  *      resolved expression, null if error
  */
-Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag)
+Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
 {
     //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars());
 
@@ -13061,9 +13190,9 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag)
         }
         if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule())
         {
-            flag = 0;
+            gag = false;
         }
-        if (flag)
+        if (gag)
             return null;
         s = ie.sds.search_correct(exp.ident);
         if (s && symbolIsVisible(sc, s))
@@ -13089,7 +13218,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag)
               ))
     {
         Type t1bn = t1b.nextOf();
-        if (flag)
+        if (gag)
         {
             if (AggregateDeclaration ad = isAggregate(t1bn))
             {
@@ -13103,11 +13232,12 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag)
          * as:
          *   (*p).ident
          */
-        if (flag && t1bn.ty == Tvoid)
+        if (gag && t1bn.ty == Tvoid)
             return null;
         Expression e = new PtrExp(exp.loc, exp.e1);
         e = e.expressionSemantic(sc);
-        return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+        const newFlag = cast(DotExpFlag) (gag * DotExpFlag.gag | exp.noderef * DotExpFlag.noDeref);
+        return e.type.dotExp(sc, e, exp.ident, newFlag);
     }
     else if (exp.ident == Id.__xalignof &&
              exp.e1.isVarExp() &&
@@ -13140,8 +13270,11 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag)
     else
     {
         if (exp.e1.isTypeExp() || exp.e1.isTemplateExp())
-            flag = 0;
-        Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+            gag = false;
+
+        const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag);
+
+        Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag);
         if (e)
         {
             e = e.expressionSemantic(sc);
@@ -13150,9 +13283,16 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag)
     }
 }
 
-// Resolve e1.ident!tiargs without seeing UFCS.
-// If flag == 1, stop "not a property" error and return NULL.
-Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int flag)
+/**
+ * Resolve `e1.ident!tiargs` without seeing UFCS.
+ * Params:
+ *     exp = the `DotTemplateInstanceExp` to resolve
+ *     sc = the semantic scope
+ *     gag = stop "not a property" error and return `null`.
+ * Returns:
+ *     `null` if error or not found, or the resolved expression.
+ */
+Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool gag)
 {
     static if (LOGSEMANTIC)
     {
@@ -13186,11 +13326,11 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int fl
             /* No built-in type has templatized properties, so do shortcut.
              * It is necessary in: 1024.max!"a < b"
              */
-            if (flag)
+            if (gag)
                 return null;
         }
-        e = die.dotIdSemanticProp(sc, flag);
-        if (flag)
+        e = die.dotIdSemanticProp(sc, gag);
+        if (gag)
         {
             if (!e ||
                 isDotOpDispatch(e))
@@ -13905,6 +14045,7 @@ Expression toBoolean(Expression exp, Scope* sc)
         case EXP.assign:
         case EXP.construct:
         case EXP.blit:
+        case EXP.loweredAssignExp:
             if (sc.flags & SCOPE.Cfile)
                 return exp;
             // Things like:
index ba2825a3098dfa80d71eb560c4529bde32bae965..7a964695f455a9baa1e1da97cdc762430d58d405 100644 (file)
@@ -299,7 +299,7 @@ void foreachExpAndVar(Statement s,
             case STMT.Conditional:
             case STMT.While:
             case STMT.Forwarding:
-            case STMT.Compile:
+            case STMT.Mixin:
             case STMT.Peel:
             case STMT.Synchronized:
                 assert(0);              // should have been rewritten
index 4b6b5b58233942cff73b4866afb87a46136dfbb6..8e11ab1bb4cd4ca85b9d14f01aa66e9f2d7c30ad 100644 (file)
@@ -204,6 +204,7 @@ private struct FUNCFLAG
     bool hasCatches;         /// function has try-catch statements
     bool skipCodegen;        /// do not generate code for this function.
     bool printf;             /// is a printf-like function
+
     bool scanf;              /// is a scanf-like function
     bool noreturn;           /// the function does not return
     bool isNRVO = true;      /// Support for named return value optimization
@@ -214,11 +215,14 @@ private struct FUNCFLAG
     bool hasNoEH;            /// No exception unwinding is needed
     bool inferRetType;       /// Return type is to be inferred
     bool hasDualContext;     /// has a dual-context 'this' parameter
+
     bool hasAlwaysInlines;   /// Contains references to functions that must be inlined
     bool isCrtCtor;          /// Has attribute pragma(crt_constructor)
     bool isCrtDtor;          /// Has attribute pragma(crt_destructor)
     bool hasEscapingSiblings;/// Has sibling functions that escape
     bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed
+    bool dllImport;          /// __declspec(dllimport)
+    bool dllExport;          /// __declspec(dllexport)
 }
 
 /***********************************************************
@@ -356,6 +360,9 @@ extern (C++) class FuncDeclaration : Declaration
     /// In case of failed `@safe` inference, store the error that made the function `@system` for
     /// better diagnostics
     AttributeViolation* safetyViolation;
+    AttributeViolation* nogcViolation;
+    AttributeViolation* pureViolation;
+    AttributeViolation* nothrowViolation;
 
     /// See the `FUNCFLAG` struct
     import dmd.common.bitfields;
@@ -370,8 +377,8 @@ extern (C++) class FuncDeclaration : Declaration
     extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false)
     {
         super(loc, ident);
-        //printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type);
-        //printf("storage_class = x%x\n", storage_class);
+        //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars());
+        //.printf("storage_class = x%llx\n", storage_class);
         this.storage_class = storage_class;
         this.type = type;
         if (type)
@@ -1294,14 +1301,14 @@ extern (C++) class FuncDeclaration : Declaration
 
     override final bool isExport() const
     {
-        return visibility.kind == Visibility.Kind.export_;
+        return visibility.kind == Visibility.Kind.export_ || dllExport;
     }
 
     override final bool isImportedSymbol() const
     {
         //printf("isImportedSymbol()\n");
         //printf("protection = %d\n", visibility);
-        return (visibility.kind == Visibility.Kind.export_) && !fbody;
+        return (visibility.kind == Visibility.Kind.export_ || dllImport) && !fbody;
     }
 
     override final bool isCodeseg() const pure nothrow @nogc @safe
@@ -1441,17 +1448,27 @@ extern (C++) class FuncDeclaration : Declaration
     }
 
     /**************************************
-     * The function is doing something impure,
-     * so mark it as impure.
-     * If there's a purity error, return true.
+     * The function is doing something impure, so mark it as impure.
+     *
+     * Params:
+     *     loc = location of impure action
+     *     fmt = format string for error message. Must include "%s `%s`" for the function kind and name.
+     *     arg0 = (optional) argument to format string
+     *
+     * Returns: `true` if there's a purity error
      */
-    extern (D) final bool setImpure()
+    extern (D) final bool setImpure(Loc loc = Loc.init, const(char)* fmt = null, RootObject arg0 = null)
     {
         if (purityInprocess)
         {
             purityInprocess = false;
+            if (fmt)
+                pureViolation = new AttributeViolation(loc, fmt, this, arg0); // impure action
+            else if (arg0)
+                pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function
+
             if (fes)
-                fes.func.setImpure();
+                fes.func.setImpure(loc, fmt, arg0);
         }
         else if (isPure())
             return true;
@@ -1540,7 +1557,7 @@ extern (C++) class FuncDeclaration : Declaration
     {
         //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
         if (nogcInprocess)
-            setGC();
+            setGC(loc, null);
         return type.toTypeFunction().isnogc;
     }
 
@@ -1552,10 +1569,16 @@ extern (C++) class FuncDeclaration : Declaration
     /**************************************
      * The function is doing something that may allocate with the GC,
      * so mark it as not nogc (not no-how).
+     *
+     * Params:
+     *     loc = location of impure action
+     *     fmt = format string for error message. Must include "%s `%s`" for the function kind and name.
+     *     arg0 = (optional) argument to format string
+     *
      * Returns:
      *      true if function is marked as @nogc, meaning a user error occurred
      */
-    extern (D) final bool setGC()
+    extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null)
     {
         //printf("setGC() %s\n", toChars());
         if (nogcInprocess && semanticRun < PASS.semantic3 && _scope)
@@ -1567,15 +1590,59 @@ extern (C++) class FuncDeclaration : Declaration
         if (nogcInprocess)
         {
             nogcInprocess = false;
+            if (fmt)
+                nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC
+            else if (arg0)
+                nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function
+
             type.toTypeFunction().isnogc = false;
             if (fes)
-                fes.func.setGC();
+                fes.func.setGC(Loc.init, null, null);
         }
         else if (isNogc())
             return true;
         return false;
     }
 
+    /**************************************
+     * The function calls non-`@nogc` function f, mark it as not nogc.
+     * Params:
+     *     f = function being called
+     * Returns:
+     *      true if function is marked as @nogc, meaning a user error occurred
+     */
+    extern (D) final bool setGCCall(FuncDeclaration f)
+    {
+        return setGC(loc, null, f);
+    }
+
+    /**************************************
+     * The function is doing something that may throw an exception, register that in case nothrow is being inferred
+     *
+     * Params:
+     *     loc = location of action
+     *     fmt = format string for error message
+     *     arg0 = (optional) argument to format string
+     */
+    extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null)
+    {
+        if (nothrowInprocess && !nothrowViolation)
+        {
+            nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC
+        }
+    }
+
+    /**************************************
+     * The function calls non-`nothrow` function f, register that in case nothrow is being inferred
+     * Params:
+     *     loc = location of call
+     *     f = function being called
+     */
+    extern (D) final void setThrowCall(Loc loc, FuncDeclaration f)
+    {
+        return setThrow(loc, null, f);
+    }
+
     extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn)
     {
         if (!global.params.vgc)
@@ -2119,7 +2186,7 @@ extern (C++) class FuncDeclaration : Declaration
         if (!needsClosure())
             return false;
 
-        if (setGC())
+        if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this))
         {
             error("is `@nogc` yet allocates closure for `%s()` with the GC", toChars());
             if (global.gag)     // need not report supplemental errors
@@ -4531,18 +4598,51 @@ struct AttributeViolation
 ///   fd = function to check
 ///   maxDepth = up to how many functions deep to report errors
 ///   deprecation = print deprecations instead of errors
-void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool deprecation)
+///   stc = storage class of attribute to check
+void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc)
 {
     auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental;
-    if (auto s = fd.safetyViolation)
+
+    AttributeViolation* s;
+    const(char)* attr;
+    if (stc & STC.safe)
+    {
+        s = fd.safetyViolation;
+        attr = "@safe";
+    }
+    else if (stc & STC.pure_)
+    {
+        s = fd.pureViolation;
+        attr = "pure";
+    }
+    else if (stc & STC.nothrow_)
+    {
+        s = fd.nothrowViolation;
+        attr = "nothrow";
+    }
+    else if (stc & STC.nogc)
+    {
+        s = fd.nogcViolation;
+        attr = "@nogc";
+    }
+
+    if (s)
     {
         if (s.fmtStr)
         {
             errorFunc(s.loc, deprecation ?
-                "which would be `@system` because of:" :
-                "which was inferred `@system` because of:");
-            errorFunc(s.loc, s.fmtStr,
-                s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : "");
+                "which wouldn't be `%s` because of:" :
+                "which wasn't inferred `%s` because of:", attr);
+            if (stc == STC.nogc || stc == STC.pure_)
+            {
+                auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration();
+                errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : "");
+            }
+            else
+            {
+                errorFunc(s.loc, s.fmtStr,
+                    s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : "");
+            }
         }
         else if (s.arg0.dyncast() == DYNCAST.dsymbol)
         {
@@ -4551,7 +4651,7 @@ void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool depr
                 if (maxDepth > 0)
                 {
                     errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars());
-                    errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation);
+                    errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc);
                 }
             }
         }
index 1919d9ae0ce1e0a53907a77443d1384100c1ed6d..45b4528c20469a25c6cad19f0d31c31594b29d3a 100644 (file)
 
 module dmd.globals;
 
+import core.stdc.stdio;
 import core.stdc.stdint;
+import core.stdc.string;
+
 import dmd.root.array;
 import dmd.root.filename;
 import dmd.common.outbuffer;
@@ -20,6 +23,8 @@ import dmd.errors;
 import dmd.file_manager;
 import dmd.identifier;
 import dmd.location;
+import dmd.lexer : CompileEnv;
+import dmd.utils;
 
 /// Defines a setting for how compiler warnings and deprecations are handled
 enum DiagnosticReporting : ubyte
@@ -149,7 +154,7 @@ extern (C++) struct Param
     FeatureState useDIP1000;     // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
     bool ehnogc;                 // use @nogc exception handling
     bool useDIP1021;             // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
-    bool fieldwise;              // do struct equality testing field-wise rather than by memcmp()
+    FeatureState fieldwise;      // do struct equality testing field-wise rather than by memcmp()
     bool fixAliasThis;           // if the current scope has an alias this, check it before searching upper scopes
     FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters
                                  // https://dconf.org/2019/talks/alexandrescu.html
@@ -209,6 +214,7 @@ extern (C++) struct Param
     bool run; // run resulting executable
     Strings runargs; // arguments for executable
     Array!(const(char)*) cppswitches;   // C preprocessor switches
+    const(char)* cpp;                   // if not null, then this specifies the C preprocessor
 
     // Linker stuff
     Array!(const(char)*) objfiles;
@@ -222,30 +228,6 @@ extern (C++) struct Param
     const(char)[] mapfile;
 }
 
-extern (C++) struct structalign_t
-{
-  private:
-    ushort value = 0;  // unknown
-    enum STRUCTALIGN_DEFAULT = 1234;   // default = match whatever the corresponding C compiler does
-    bool pack;         // use #pragma pack semantics
-
-  public:
-  pure @safe @nogc nothrow:
-    bool isDefault() const { return value == STRUCTALIGN_DEFAULT; }
-    void setDefault()      { value = STRUCTALIGN_DEFAULT; }
-    bool isUnknown() const { return value == 0; }  // value is not set
-    void setUnknown()      { value = 0; }
-    void set(uint value)   { this.value = cast(ushort)value; }
-    uint get() const       { return value; }
-    bool isPack() const    { return pack; }
-    void setPack(bool pack) { this.pack = pack; }
-}
-//alias structalign_t = uint;
-
-// magic value means "match whatever the underlying C compiler does"
-// other values are all powers of 2
-//enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
-
 enum mars_ext = "d";        // for D source files
 enum doc_ext  = "html";     // for Ddoc generated files
 enum ddoc_ext = "ddoc";     // for Ddoc macro include files
@@ -270,9 +252,7 @@ extern (C++) struct Global
     Array!(const(char)*)* filePath;     /// Array of char*'s which form the file import lookup path
 
     private enum string _version = import("VERSION");
-    private enum uint _versionNumber = parseVersionNumber(_version);
-
-    const(char)[] vendor;   /// Compiler backend name
+    CompileEnv compileEnv;
 
     Param params;           /// command line parameters
     uint errors;            /// number of errors reported so far
@@ -350,12 +330,12 @@ extern (C++) struct Global
 
     extern (C++) void _init()
     {
-        global.errorSink = new ErrorSinkCompiler;
+        errorSink = new ErrorSinkCompiler;
 
         this.fileManager = new FileManager();
         version (MARS)
         {
-            vendor = "Digital Mars D";
+            compileEnv.vendor = "Digital Mars D";
 
             // -color=auto is the default value
             import dmd.console : detectTerminal;
@@ -363,8 +343,38 @@ extern (C++) struct Global
         }
         else version (IN_GCC)
         {
-            vendor = "GNU D";
+            compileEnv.vendor = "GNU D";
+        }
+        compileEnv.versionNumber = parseVersionNumber(_version);
+
+        /* Initialize date, time, and timestamp
+         */
+        import core.stdc.time;
+        import core.stdc.stdlib : getenv;
+
+        time_t ct;
+        // https://issues.dlang.org/show_bug.cgi?id=20444
+        if (auto p = getenv("SOURCE_DATE_EPOCH"))
+        {
+            if (!ct.parseDigits(p[0 .. strlen(p)]))
+                errorSink.error(Loc.initial, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p);
         }
+        else
+            core.stdc.time.time(&ct);
+        const p = ctime(&ct);
+        assert(p);
+
+        __gshared char[11 + 1] date = 0;        // put in BSS segment
+        __gshared char[8  + 1] time = 0;
+        __gshared char[24 + 1] timestamp = 0;
+
+        const dsz = snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20);
+        const tsz = snprintf(&time[0], time.length, "%.8s", p + 11);
+        const tssz = snprintf(&timestamp[0], timestamp.length, "%.24s", p);
+        assert(dsz > 0 && tsz > 0 && tssz > 0);
+        compileEnv.time = time[0 .. tsz];
+        compileEnv.date = date[0 .. dsz];
+        compileEnv.timestamp = timestamp[0 .. tssz];
     }
 
     /**
@@ -415,7 +425,7 @@ extern (C++) struct Global
     */
     extern(C++) uint versionNumber()
     {
-        return _versionNumber;
+        return compileEnv.versionNumber;
     }
 
     /**
index 84fbec6977d96c937762ede0a5a23c4a1b7a27f3..902cf83f81f5e77aece04af63276c6550114bdc5 100644 (file)
@@ -151,7 +151,7 @@ struct Param
     FeatureState useDIP1000;     // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
     d_bool ehnogc;                 // use @nogc exception handling
     d_bool useDIP1021;             // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
-    d_bool fieldwise;              // do struct equality testing field-wise rather than by memcmp()
+    FeatureState fieldwise;      // do struct equality testing field-wise rather than by memcmp()
     d_bool fixAliasThis;           // if the current scope has an alias this, check it before searching upper scopes
     FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters
                                  // https://dconf.org/2019/talks/alexandrescu.html
@@ -212,6 +212,7 @@ struct Param
     Strings runargs;    // arguments for executable
 
     Array<const char *> cppswitches; // preprocessor switches
+    const char *cpp;                 // if not null, then this specifies the C preprocessor
 
     // Linker stuff
     Array<const char *> objfiles;
@@ -252,6 +253,18 @@ const DString hdr_ext  = "di";       // for D 'header' import files
 const DString json_ext = "json";     // for JSON files
 const DString map_ext  = "map";      // for .map files
 
+struct CompileEnv
+{
+    uint32_t versionNumber;
+    DString date;
+    DString time;
+    DString vendor;
+    DString timestamp;
+    bool previewIn;
+    bool ddocOutput;
+    bool shortenedMethods;
+};
+
 struct Global
 {
     DString inifilename;
@@ -261,7 +274,7 @@ struct Global
     Array<const char *> *path;        // Array of char*'s which form the import lookup path
     Array<const char *> *filePath;    // Array of char*'s which form the file import lookup path
 
-    DString vendor;          // Compiler backend name
+    CompileEnv compileEnv;
 
     Param params;
     unsigned errors;         // number of errors reported so far
index e0684e6b36542a78581bc34290f4abfa2fb43fad..a159c2f15617d69ad4abf18646fa9f3e3ed1b634 100644 (file)
@@ -134,37 +134,19 @@ void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs)
 
 private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs)
 {
-    scope v = new StatementPrettyPrintVisitor(buf, hgs);
-    s.accept(v);
-}
-
-private extern (C++) final class StatementPrettyPrintVisitor : Visitor
-{
-    alias visit = Visitor.visit;
-public:
-    OutBuffer* buf;
-    HdrGenState* hgs;
-
-    extern (D) this(OutBuffer* buf, HdrGenState* hgs) scope
+    void visitDefaultCase(Statement s)
     {
-        this.buf = buf;
-        this.hgs = hgs;
+        printf("Statement::toCBuffer() %d\n", s.stmt);
+        assert(0, "unrecognized statement in statementToBuffer()");
     }
 
-    override void visit(Statement s)
-    {
-        buf.writestring("Statement::toCBuffer()");
-        buf.writenl();
-        assert(0);
-    }
-
-    override void visit(ErrorStatement s)
+    void visitError(ErrorStatement s)
     {
         buf.writestring("__error__");
         buf.writenl();
     }
 
-    override void visit(ExpStatement s)
+    void visitExp(ExpStatement s)
     {
         if (s.exp && s.exp.op == EXP.declaration &&
             (cast(DeclarationExp)s.exp).declaration)
@@ -180,7 +162,12 @@ public:
             buf.writenl();
     }
 
-    override void visit(CompileStatement s)
+    void visitDtorExp(DtorExpStatement s)
+    {
+        visitExp(s);
+    }
+
+    void visitMixin(MixinStatement s)
     {
         buf.writestring("mixin(");
         argsToBuffer(s.exps, buf, hgs, null);
@@ -189,25 +176,29 @@ public:
             buf.writenl();
     }
 
-    override void visit(CompoundStatement s)
+    void visitCompound(CompoundStatement s)
     {
         foreach (sx; *s.statements)
         {
             if (sx)
-                sx.accept(this);
+                sx.statementToBuffer(buf, hgs);
         }
     }
 
-    override void visit(CompoundDeclarationStatement s)
+    void visitCompoundAsm(CompoundAsmStatement s)
+    {
+        visitCompound(s);
+    }
+
+    void visitCompoundDeclaration(CompoundDeclarationStatement s)
     {
         bool anywritten = false;
         foreach (sx; *s.statements)
         {
             auto ds = sx ? sx.isExpStatement() : null;
-            if (ds && ds.exp.op == EXP.declaration)
+            if (ds && ds.exp.isDeclarationExp())
             {
-                auto d = (cast(DeclarationExp)ds.exp).declaration;
-                assert(d.isDeclaration());
+                auto d = ds.exp.isDeclarationExp().declaration;
                 if (auto v = d.isVarDeclaration())
                 {
                     scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs);
@@ -223,7 +214,7 @@ public:
             buf.writenl();
     }
 
-    override void visit(UnrolledLoopStatement s)
+    void visitUnrolledLoop(UnrolledLoopStatement s)
     {
         buf.writestring("/*unrolled*/ {");
         buf.writenl();
@@ -231,26 +222,26 @@ public:
         foreach (sx; *s.statements)
         {
             if (sx)
-                sx.accept(this);
+                sx.statementToBuffer(buf, hgs);
         }
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
     }
 
-    override void visit(ScopeStatement s)
+    void visitScope(ScopeStatement s)
     {
         buf.writeByte('{');
         buf.writenl();
         buf.level++;
         if (s.statement)
-            s.statement.accept(this);
+            s.statement.statementToBuffer(buf, hgs);
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
     }
 
-    override void visit(WhileStatement s)
+    void visitWhile(WhileStatement s)
     {
         buf.writestring("while (");
         if (auto p = s.param)
@@ -271,28 +262,28 @@ public:
         buf.writeByte(')');
         buf.writenl();
         if (s._body)
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
     }
 
-    override void visit(DoStatement s)
+    void visitDo(DoStatement s)
     {
         buf.writestring("do");
         buf.writenl();
         if (s._body)
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
         buf.writestring("while (");
         s.condition.expressionToBuffer(buf, hgs);
         buf.writestring(");");
         buf.writenl();
     }
 
-    override void visit(ForStatement s)
+    void visitFor(ForStatement s)
     {
         buf.writestring("for (");
         if (s._init)
         {
             hgs.forStmtInit++;
-            s._init.accept(this);
+            s._init.statementToBuffer(buf, hgs);
             hgs.forStmtInit--;
         }
         else
@@ -314,13 +305,13 @@ public:
         buf.writenl();
         buf.level++;
         if (s._body)
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
     }
 
-    private void foreachWithoutBody(ForeachStatement s)
+    void foreachWithoutBody(ForeachStatement s)
     {
         buf.writestring(Token.toString(s.op));
         buf.writestring(" (");
@@ -341,20 +332,20 @@ public:
         buf.writenl();
     }
 
-    override void visit(ForeachStatement s)
+    void visitForeach(ForeachStatement s)
     {
         foreachWithoutBody(s);
         buf.writeByte('{');
         buf.writenl();
         buf.level++;
         if (s._body)
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
     }
 
-    private void foreachRangeWithoutBody(ForeachRangeStatement s)
+    void foreachRangeWithoutBody(ForeachRangeStatement s)
     {
         buf.writestring(Token.toString(s.op));
         buf.writestring(" (");
@@ -370,39 +361,39 @@ public:
         buf.writenl();
     }
 
-    override void visit(ForeachRangeStatement s)
+    void visitForeachRange(ForeachRangeStatement s)
     {
         foreachRangeWithoutBody(s);
         buf.writeByte('{');
         buf.writenl();
         buf.level++;
         if (s._body)
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
     }
 
-    override void visit(StaticForeachStatement s)
+    void visitStaticForeach(StaticForeachStatement s)
     {
         buf.writestring("static ");
         if (s.sfe.aggrfe)
         {
-            visit(s.sfe.aggrfe);
+            visitForeach(s.sfe.aggrfe);
         }
         else
         {
             assert(s.sfe.rangefe);
-            visit(s.sfe.rangefe);
+            visitForeachRange(s.sfe.rangefe);
         }
     }
 
-    override void visit(ForwardingStatement s)
+    void visitForwarding(ForwardingStatement s)
     {
-        s.statement.accept(this);
+        s.statement.statementToBuffer(buf, hgs);
     }
 
-    override void visit(IfStatement s)
+    void visitIf(IfStatement s)
     {
         buf.writestring("if (");
         if (Parameter p = s.prm)
@@ -423,12 +414,12 @@ public:
         buf.writenl();
         if (s.ifbody.isScopeStatement())
         {
-            s.ifbody.accept(this);
+            s.ifbody.statementToBuffer(buf, hgs);
         }
         else
         {
             buf.level++;
-            s.ifbody.accept(this);
+            s.ifbody.statementToBuffer(buf, hgs);
             buf.level--;
         }
         if (s.elsebody)
@@ -444,18 +435,18 @@ public:
             }
             if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
             {
-                s.elsebody.accept(this);
+                s.elsebody.statementToBuffer(buf, hgs);
             }
             else
             {
                 buf.level++;
-                s.elsebody.accept(this);
+                s.elsebody.statementToBuffer(buf, hgs);
                 buf.level--;
             }
         }
     }
 
-    override void visit(ConditionalStatement s)
+    void visitConditional(ConditionalStatement s)
     {
         s.condition.conditionToBuffer(buf, hgs);
         buf.writenl();
@@ -463,7 +454,7 @@ public:
         buf.writenl();
         buf.level++;
         if (s.ifbody)
-            s.ifbody.accept(this);
+            s.ifbody.statementToBuffer(buf, hgs);
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
@@ -474,14 +465,14 @@ public:
             buf.writeByte('{');
             buf.level++;
             buf.writenl();
-            s.elsebody.accept(this);
+            s.elsebody.statementToBuffer(buf, hgs);
             buf.level--;
             buf.writeByte('}');
         }
         buf.writenl();
     }
 
-    override void visit(PragmaStatement s)
+    void visitPragma(PragmaStatement s)
     {
         buf.writestring("pragma (");
         buf.writestring(s.ident.toString());
@@ -497,7 +488,7 @@ public:
             buf.writeByte('{');
             buf.writenl();
             buf.level++;
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
             buf.level--;
             buf.writeByte('}');
             buf.writenl();
@@ -509,12 +500,12 @@ public:
         }
     }
 
-    override void visit(StaticAssertStatement s)
+    void visitStaticAssert(StaticAssertStatement s)
     {
         s.sa.dsymbolToBuffer(buf, hgs);
     }
 
-    override void visit(SwitchStatement s)
+    void visitSwitch(SwitchStatement s)
     {
         buf.writestring(s.isFinal ? "final switch (" : "switch (");
         s.condition.expressionToBuffer(buf, hgs);
@@ -527,28 +518,28 @@ public:
                 buf.writeByte('{');
                 buf.writenl();
                 buf.level++;
-                s._body.accept(this);
+                s._body.statementToBuffer(buf, hgs);
                 buf.level--;
                 buf.writeByte('}');
                 buf.writenl();
             }
             else
             {
-                s._body.accept(this);
+                s._body.statementToBuffer(buf, hgs);
             }
         }
     }
 
-    override void visit(CaseStatement s)
+    void visitCase(CaseStatement s)
     {
         buf.writestring("case ");
         s.exp.expressionToBuffer(buf, hgs);
         buf.writeByte(':');
         buf.writenl();
-        s.statement.accept(this);
+        s.statement.statementToBuffer(buf, hgs);
     }
 
-    override void visit(CaseRangeStatement s)
+    void visitCaseRange(CaseRangeStatement s)
     {
         buf.writestring("case ");
         s.first.expressionToBuffer(buf, hgs);
@@ -556,23 +547,23 @@ public:
         s.last.expressionToBuffer(buf, hgs);
         buf.writeByte(':');
         buf.writenl();
-        s.statement.accept(this);
+        s.statement.statementToBuffer(buf, hgs);
     }
 
-    override void visit(DefaultStatement s)
+    void visitDefault(DefaultStatement s)
     {
         buf.writestring("default:");
         buf.writenl();
-        s.statement.accept(this);
+        s.statement.statementToBuffer(buf, hgs);
     }
 
-    override void visit(GotoDefaultStatement s)
+    void visitGotoDefault(GotoDefaultStatement s)
     {
         buf.writestring("goto default;");
         buf.writenl();
     }
 
-    override void visit(GotoCaseStatement s)
+    void visitGotoCase(GotoCaseStatement s)
     {
         buf.writestring("goto case");
         if (s.exp)
@@ -584,13 +575,13 @@ public:
         buf.writenl();
     }
 
-    override void visit(SwitchErrorStatement s)
+    void visitSwitchError(SwitchErrorStatement s)
     {
         buf.writestring("SwitchErrorStatement::toCBuffer()");
         buf.writenl();
     }
 
-    override void visit(ReturnStatement s)
+    void visitReturn(ReturnStatement s)
     {
         buf.writestring("return ");
         if (s.exp)
@@ -599,7 +590,7 @@ public:
         buf.writenl();
     }
 
-    override void visit(BreakStatement s)
+    void visitBreak(BreakStatement s)
     {
         buf.writestring("break");
         if (s.ident)
@@ -611,7 +602,7 @@ public:
         buf.writenl();
     }
 
-    override void visit(ContinueStatement s)
+    void visitContinue(ContinueStatement s)
     {
         buf.writestring("continue");
         if (s.ident)
@@ -623,7 +614,7 @@ public:
         buf.writenl();
     }
 
-    override void visit(SynchronizedStatement s)
+    void visitSynchronized(SynchronizedStatement s)
     {
         buf.writestring("synchronized");
         if (s.exp)
@@ -635,21 +626,21 @@ public:
         if (s._body)
         {
             buf.writeByte(' ');
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
         }
     }
 
-    override void visit(WithStatement s)
+    void visitWith(WithStatement s)
     {
         buf.writestring("with (");
         s.exp.expressionToBuffer(buf, hgs);
         buf.writestring(")");
         buf.writenl();
         if (s._body)
-            s._body.accept(this);
+            s._body.statementToBuffer(buf, hgs);
     }
 
-    override void visit(TryCatchStatement s)
+    void visitTryCatch(TryCatchStatement s)
     {
         buf.writestring("try");
         buf.writenl();
@@ -657,29 +648,44 @@ public:
         {
             if (s._body.isScopeStatement())
             {
-                s._body.accept(this);
+                s._body.statementToBuffer(buf, hgs);
             }
             else
             {
                 buf.level++;
-                s._body.accept(this);
+                s._body.statementToBuffer(buf, hgs);
                 buf.level--;
             }
         }
         foreach (c; *s.catches)
         {
-            visit(c);
+            buf.writestring("catch");
+            if (c.type)
+            {
+                buf.writeByte('(');
+                typeToBuffer(c.type, c.ident, buf, hgs);
+                buf.writeByte(')');
+            }
+            buf.writenl();
+            buf.writeByte('{');
+            buf.writenl();
+            buf.level++;
+            if (c.handler)
+                c.handler.statementToBuffer(buf, hgs);
+            buf.level--;
+            buf.writeByte('}');
+            buf.writenl();
         }
     }
 
-    override void visit(TryFinallyStatement s)
+    void visitTryFinally(TryFinallyStatement s)
     {
         buf.writestring("try");
         buf.writenl();
         buf.writeByte('{');
         buf.writenl();
         buf.level++;
-        s._body.accept(this);
+        s._body.statementToBuffer(buf, hgs);
         buf.level--;
         buf.writeByte('}');
         buf.writenl();
@@ -687,25 +693,25 @@ public:
         buf.writenl();
         if (s.finalbody.isScopeStatement())
         {
-            s.finalbody.accept(this);
+            s.finalbody.statementToBuffer(buf, hgs);
         }
         else
         {
             buf.level++;
-            s.finalbody.accept(this);
+            s.finalbody.statementToBuffer(buf, hgs);
             buf.level--;
         }
     }
 
-    override void visit(ScopeGuardStatement s)
+    void visitScopeGuard(ScopeGuardStatement s)
     {
         buf.writestring(Token.toString(s.tok));
         buf.writeByte(' ');
         if (s.statement)
-            s.statement.accept(this);
+            s.statement.statementToBuffer(buf, hgs);
     }
 
-    override void visit(ThrowStatement s)
+    void visitThrow(ThrowStatement s)
     {
         buf.writestring("throw ");
         s.exp.expressionToBuffer(buf, hgs);
@@ -713,15 +719,15 @@ public:
         buf.writenl();
     }
 
-    override void visit(DebugStatement s)
+    void visitDebug(DebugStatement s)
     {
         if (s.statement)
         {
-            s.statement.accept(this);
+            s.statement.statementToBuffer(buf, hgs);
         }
     }
 
-    override void visit(GotoStatement s)
+    void visitGoto(GotoStatement s)
     {
         buf.writestring("goto ");
         buf.writestring(s.ident.toString());
@@ -729,16 +735,16 @@ public:
         buf.writenl();
     }
 
-    override void visit(LabelStatement s)
+    void visitLabel(LabelStatement s)
     {
         buf.writestring(s.ident.toString());
         buf.writeByte(':');
         buf.writenl();
         if (s.statement)
-            s.statement.accept(this);
+            s.statement.statementToBuffer(buf, hgs);
     }
 
-    override void visit(AsmStatement s)
+    void visitAsm(AsmStatement s)
     {
         buf.writestring("asm { ");
         Token* t = s.tokens;
@@ -764,33 +770,26 @@ public:
         buf.writenl();
     }
 
-    override void visit(ImportStatement s)
+    void visitInlineAsm(InlineAsmStatement s)
     {
-        foreach (imp; *s.imports)
-        {
-            imp.dsymbolToBuffer(buf, hgs);
-        }
+        visitAsm(s);
     }
 
-    void visit(Catch c)
+    void visitGccAsm(GccAsmStatement s)
     {
-        buf.writestring("catch");
-        if (c.type)
+        visitAsm(s);
+    }
+
+    void visitImport(ImportStatement s)
+    {
+        foreach (imp; *s.imports)
         {
-            buf.writeByte('(');
-            typeToBuffer(c.type, c.ident, buf, hgs);
-            buf.writeByte(')');
+            imp.dsymbolToBuffer(buf, hgs);
         }
-        buf.writenl();
-        buf.writeByte('{');
-        buf.writenl();
-        buf.level++;
-        if (c.handler)
-            c.handler.accept(this);
-        buf.level--;
-        buf.writeByte('}');
-        buf.writenl();
     }
+
+    mixin VisitStatement!void visit;
+    visit.VisitStatement(s);
 }
 
 private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
@@ -1160,7 +1159,7 @@ public:
 
     }
 
-    override void visit(CompileDeclaration d)
+    override void visit(MixinDeclaration d)
     {
         buf.writestring("mixin(");
         argsToBuffer(d.exps, buf, hgs, null);
@@ -2321,6 +2320,16 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
+    void visitLoweredAssignExp(LoweredAssignExp e)
+    {
+        if (global.params.vcg_ast)
+        {
+            expressionToBuffer(e.lowering, buf, hgs);
+            return;
+        }
+
+        visit(cast(BinExp)e);
+    }
     void visitBin(BinExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
@@ -2694,6 +2703,7 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg
         case EXP.remove:        return visitRemove(e.isRemoveExp());
         case EXP.question:      return visitCond(e.isCondExp());
         case EXP.classReference:        return visitClassReference(e.isClassReferenceExp());
+        case EXP.loweredAssignExp:      return visitLoweredAssignExp(e.isLoweredAssignExp());
     }
 }
 
@@ -2882,8 +2892,7 @@ public:
 
 void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
 {
-    scope v = new StatementPrettyPrintVisitor(buf, hgs);
-    (cast() s).accept(v);
+    (cast()s).statementToBuffer(buf, hgs);
 }
 
 void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs)
@@ -3223,6 +3232,7 @@ private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* h
     final switch (pl.varargs)
     {
         case VarArg.none:
+        case VarArg.KRvariadic:
             break;
 
         case VarArg.variadic:
@@ -3817,15 +3827,8 @@ private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* h
         buf.writeByte('}');
     }
 
-    final switch (inx.kind)
-    {
-        case InitKind.error:   return visitError (inx.isErrorInitializer ());
-        case InitKind.void_:   return visitVoid  (inx.isVoidInitializer  ());
-        case InitKind.struct_: return visitStruct(inx.isStructInitializer());
-        case InitKind.array:   return visitArray (inx.isArrayInitializer ());
-        case InitKind.exp:     return visitExp   (inx.isExpInitializer   ());
-        case InitKind.C_:      return visitC     (inx.isCInitializer     ());
-    }
+    mixin VisitInitializer!void visit;
+    visit.VisitInitializer(inx);
 }
 
 
@@ -4101,7 +4104,6 @@ string EXPtoString(EXP op)
         EXP.error : "error",
         EXP.objcClassReference : "class",
 
-        EXP.typeof_ : "typeof",
         EXP.mixin_ : "mixin",
 
         EXP.import_ : "import",
@@ -4141,7 +4143,6 @@ string EXPtoString(EXP op)
         EXP.remove : "remove",
         EXP.tuple : "tuple",
         EXP.traits : "__traits",
-        EXP.default_ : "default",
         EXP.overloadSet : "__overloadset",
         EXP.void_ : "void",
         EXP.vectorArray : "vectorarray",
@@ -4232,6 +4233,7 @@ string EXPtoString(EXP op)
         EXP.declaration : "declaration",
 
         EXP.interval : "interval",
+        EXP.loweredAssignExp : "="
     ];
     const p = strings[op];
     if (!p)
index 4d780b302b09b7e7501d21ce2e5a05cef7a525d3..66829da866760d637dc0ce1af1036379dbe53c7b 100644 (file)
 
 module dmd.iasm;
 
+import core.stdc.stdio;
+
 import dmd.dscope;
+import dmd.expression;
 import dmd.func;
+import dmd.mtype;
+import dmd.tokens;
 import dmd.statement;
+import dmd.statementsem;
 
 version (MARS)
 {
@@ -43,6 +49,19 @@ extern(C++) Statement asmSemantic(AsmStatement s, Scope *sc)
 
     version (MARS)
     {
+        /* If it starts with a string literal, it's gcc inline asm
+         */
+        if (s.tokens.value == TOK.string_)
+        {
+            /* Replace the asm statement with an assert(0, msg) that trips at runtime.
+             */
+            const loc = s.loc;
+            auto e = new IntegerExp(loc, 0, Type.tint32);
+            auto msg = new StringExp(loc, "Gnu Asm not supported - compile this function with gcc or clang");
+            auto ae = new AssertExp(loc, e, msg);
+            auto se = new ExpStatement(loc, ae);
+            return statementSemantic(se, sc);
+        }
         auto ias = new InlineAsmStatement(s.loc, s.tokens);
         return inlineAsmSemantic(ias, sc);
     }
index f8c88ab536e9cccd5cfa2d2829244f07bdc4d652..1d4dea47b8150beee6ccc33625d0ecd9ef0e903a 100644 (file)
@@ -302,7 +302,8 @@ Ldone:
 extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
 {
     //printf("GccAsmStatement.semantic()\n");
-    scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink);
+    const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+    scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
 
     // Make a safe copy of the token list before parsing.
     Token *toklist = null;
@@ -410,7 +411,8 @@ unittest
     {
         const errors = global.errors;
         scope gas = new GccAsmStatement(Loc.initial, tokens);
-        scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink);
+        const bool doUnittests = false;
+        scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests);
         p.token = *tokens;
         p.parseGccAsm(gas);
         return global.errors - errors;
@@ -420,7 +422,8 @@ unittest
     static void parseAsm(string input, bool expectError)
     {
         // Generate tokens from input test.
-        scope p = new Parser!ASTCodegen(null, input, false, global.errorSink);
+        const bool doUnittests = false;
+        scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests);
         p.nextToken();
 
         Token* toklist = null;
index ec5cb25ef28abf5ee25ff96481bb186f580fc8e8..a2271d510179c54f0a327fc01ea792a338be90e1 100644 (file)
@@ -361,6 +361,8 @@ immutable Msgtable[] msgtable =
     { "_d_arrayappendcTXImpl" },
     { "_d_arrayappendcTX" },
     { "_d_arrayappendcTXTrace" },
+    { "_d_arraycatnTX" },
+    { "_d_arraycatnTXTrace" },
 
     // varargs implementation
     { "stdc" },
@@ -370,6 +372,10 @@ immutable Msgtable[] msgtable =
     // Builtin functions
     { "std" },
     { "core" },
+    { "config" },
+    { "c_complex_float" },
+    { "c_complex_double" },
+    { "c_complex_real" },
     { "etc" },
     { "attribute" },
     { "atomic" },
@@ -519,9 +525,17 @@ immutable Msgtable[] msgtable =
     { "__tag" },
     { "dllimport" },
     { "dllexport" },
+    { "naked" },
+    { "thread" },
     { "vector_size" },
     { "__func__" },
+    { "always_inline" },
+    { "noinline" },
     { "noreturn" },
+    { "_nothrow", "nothrow" },
+    { "_deprecated", "deprecated" },
+    { "_align", "align" },
+    { "aligned" },
     { "__pragma", "pragma" },
     { "builtins", "__builtins" },
     { "builtin_va_list", "__builtin_va_list" },
@@ -532,6 +546,7 @@ immutable Msgtable[] msgtable =
     { "show" },
     { "push" },
     { "pop" },
+    { "_pure", "pure" },
     { "define" },
     { "undef" },
 ];
index f646d0382eb6e714f9084383e71249600e569afb..6f20a3c7a45866a69909e976a33103bc93aad3d4 100644 (file)
@@ -269,7 +269,22 @@ extern (C++) final class CInitializer : Initializer
  */
 Initializer syntaxCopy(Initializer inx)
 {
-    static Initializer copyStruct(StructInitializer vi)
+    static Initializer visitVoid(VoidInitializer vi)
+    {
+        return new VoidInitializer(vi.loc);
+    }
+
+    static Initializer visitError(ErrorInitializer vi)
+    {
+        return vi;
+    }
+
+    static Initializer visitExp(ExpInitializer vi)
+    {
+        return new ExpInitializer(vi.loc, vi.exp.syntaxCopy());
+    }
+
+    static Initializer visitStruct(StructInitializer vi)
     {
         auto si = new StructInitializer(vi.loc);
         assert(vi.field.length == vi.value.length);
@@ -283,7 +298,7 @@ Initializer syntaxCopy(Initializer inx)
         return si;
     }
 
-    static Initializer copyArray(ArrayInitializer vi)
+    static Initializer visitArray(ArrayInitializer vi)
     {
         auto ai = new ArrayInitializer(vi.loc);
         assert(vi.index.length == vi.value.length);
@@ -297,7 +312,7 @@ Initializer syntaxCopy(Initializer inx)
         return ai;
     }
 
-    static Initializer copyC(CInitializer vi)
+    static Initializer visitC(CInitializer vi)
     {
         auto ci = new CInitializer(vi.loc);
         ci.initializerList.setDim(vi.initializerList.length);
@@ -322,13 +337,62 @@ Initializer syntaxCopy(Initializer inx)
         return ci;
     }
 
-    final switch (inx.kind)
+    mixin VisitInitializer!Initializer visit;
+    return visit.VisitInitializer(inx);
+}
+
+/***********************************************************
+ * Visit each Initializer in init. Call a function visit%s(init) for
+ * each node, where %s is the op of the node. Otherwise call visitDefault(init)
+ * for that node. If the visit function returns R.init, continue
+ * visiting each node, otherwise return the value of R.
+ * Params:
+ *      Result = return type
+ *      init = Initializer tree to traverse
+ * Returns:
+ *      Result.init for continue, value of type Result for early exit
+ */
+
+mixin template VisitInitializer(Result)
+{
+    Result VisitInitializer(Initializer init)
+    {
+        final switch (init.kind)
+        {
+            case InitKind.void_:    mixin(visitCase("Void"));    break;
+            case InitKind.error:    mixin(visitCase("Error"));   break;
+            case InitKind.struct_:  mixin(visitCase("Struct"));  break;
+            case InitKind.array:    mixin(visitCase("Array"));   break;
+            case InitKind.exp:      mixin(visitCase("Exp"));     break;
+            case InitKind.C_:       mixin(visitCase("C"));       break;
+        }
+        static if (is(Result == void)) { } else
+            return Result.init;
+    }
+}
+
+/****************************************
+ * CTFE-only helper function for VisitInitializer.
+ * Params:
+ *      handler = string for the name of the visit handler
+ * Returns: boilerplate code for a case
+ */
+pure string visitCase(string handler)
+{
+    if (__ctfe)
     {
-        case InitKind.void_:   return new VoidInitializer(inx.loc);
-        case InitKind.error:   return inx;
-        case InitKind.struct_: return copyStruct(cast(StructInitializer)inx);
-        case InitKind.array:   return copyArray(cast(ArrayInitializer)inx);
-        case InitKind.exp:     return new ExpInitializer(inx.loc, (cast(ExpInitializer)inx).exp.syntaxCopy());
-        case InitKind.C_:      return copyC(cast(CInitializer)inx);
+        return
+            "
+            auto ix = init.is"~handler~"Initializer();
+            static if (is(Result == void))
+                visit"~handler~"(ix);
+            else
+            {
+                Result r = visit"~handler~"(ix);
+                if (r !is Result.init)
+                    return r;
+            }
+            ";
     }
+    assert(0);
 }
index 18b10b41a2d3e19acacca01bd7ccc080acaef681..893d2a627c3be156a6672d9afbaba3ee78b6d447 100644 (file)
@@ -958,15 +958,9 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         }
     }
 
-    final switch (init.kind)
-    {
-        case InitKind.void_:   return visitVoid  (init.isVoidInitializer());
-        case InitKind.error:   return visitError (init.isErrorInitializer());
-        case InitKind.struct_: return visitStruct(init.isStructInitializer());
-        case InitKind.array:   return visitArray (init.isArrayInitializer());
-        case InitKind.exp:     return visitExp   (init.isExpInitializer());
-        case InitKind.C_:      return visitC     (init.isCInitializer());
-    }
+    mixin VisitInitializer!Initializer visit;
+    auto result = visit.VisitInitializer(init);
+    return (result !is null) ? result : new ErrorInitializer();
 }
 
 /***********************
@@ -1120,15 +1114,9 @@ Initializer inferType(Initializer init, Scope* sc)
         return new ErrorInitializer();
     }
 
-    final switch (init.kind)
-    {
-        case InitKind.void_:   return visitVoid  (init.isVoidInitializer());
-        case InitKind.error:   return visitError (init.isErrorInitializer());
-        case InitKind.struct_: return visitStruct(init.isStructInitializer());
-        case InitKind.array:   return visitArray (init.isArrayInitializer());
-        case InitKind.exp:     return visitExp   (init.isExpInitializer());
-        case InitKind.C_:      return visitC     (init.isCInitializer());
-    }
+    mixin VisitInitializer!Initializer visit;
+    auto result = visit.VisitInitializer(init);
+    return (result !is null) ? result : new ErrorInitializer();
 }
 
 /***********************
@@ -1333,15 +1321,8 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n
         return null;
     }
 
-    final switch (init.kind)
-    {
-        case InitKind.void_:   return visitVoid  (init.isVoidInitializer());
-        case InitKind.error:   return visitError (init.isErrorInitializer());
-        case InitKind.struct_: return visitStruct(init.isStructInitializer());
-        case InitKind.array:   return visitArray (init.isArrayInitializer());
-        case InitKind.exp:     return visitExp   (init.isExpInitializer());
-        case InitKind.C_:      return visitC     (init.isCInitializer());
-    }
+    mixin VisitInitializer!Expression visit;
+    return visit.VisitInitializer(init);
 }
 
 
index 2af7faec354d6c74eedb6896a887cc174ff3548f..dcf53b8f547434f65ef7a6c4bcb2f237a7848a53 100644 (file)
@@ -833,7 +833,7 @@ public:
     {
         import dmd.target : target;
         objectStart();
-        requiredProperty("vendor", global.vendor);
+        requiredProperty("vendor", global.compileEnv.vendor);
         requiredProperty("version", global.versionString());
         property("__VERSION__", global.versionNumber());
         requiredProperty("interface", determineCompilerInterface());
@@ -1070,13 +1070,13 @@ Determines and returns the compiler interface which is one of `dmd`, `ldc`,
 */
 private extern(D) string determineCompilerInterface()
 {
-    if (global.vendor == "Digital Mars D")
+    if (global.compileEnv.vendor == "Digital Mars D")
         return "dmd";
-    if (global.vendor == "LDC")
+    if (global.compileEnv.vendor == "LDC")
         return "ldc";
-    if (global.vendor == "GNU D")
+    if (global.compileEnv.vendor == "GNU D")
         return "gdc";
-    if (global.vendor == "SDC")
+    if (global.compileEnv.vendor == "SDC")
         return "sdc";
     return null;
 }
index f0f7872c2b214aed8ff99f90b61177e13d0bf13f..0ec468b3245b2d1d52044fe1ecd23bb2276e7796 100644 (file)
 module dmd.lexer;
 
 import core.stdc.ctype;
-import core.stdc.errno;
-import core.stdc.stdarg;
 import core.stdc.stdio;
-import core.stdc.stdlib : getenv;
 import core.stdc.string;
-import core.stdc.time;
 
 import dmd.entity;
 import dmd.errorsink;
@@ -31,10 +27,8 @@ import dmd.root.ctfloat;
 import dmd.common.outbuffer;
 import dmd.root.port;
 import dmd.root.rmem;
-import dmd.root.string;
 import dmd.root.utf;
 import dmd.tokens;
-import dmd.utils;
 
 nothrow:
 
@@ -43,6 +37,22 @@ version (DMDLIB)
     version = LocOffset;
 }
 
+/***********************************************************
+ * Values to use for various magic identifiers
+ */
+struct CompileEnv
+{
+    uint versionNumber;      /// __VERSION__
+    const(char)[] date;      /// __DATE__
+    const(char)[] time;      /// __TIME__
+    const(char)[] vendor;    /// __VENDOR__
+    const(char)[] timestamp; /// __TIMESTAMP__
+
+    bool previewIn;          /// `in` means `[ref] scope const`, accepts rvalues
+    bool ddocOutput;         /// collect embedded documentation comments
+    bool shortenedMethods = true;   /// allow => in normal function declarations
+}
+
 /***********************************************************
  */
 class Lexer
@@ -69,6 +79,7 @@ class Lexer
     ubyte wchar_tsize;          /// size of C wchar_t, 2 or 4
 
     ErrorSink eSink;            /// send error messages through this interface
+    CompileEnv compileEnv;      /// environment
 
     private
     {
@@ -87,8 +98,6 @@ class Lexer
         int lastDocLine;        // last line of previous doc comment
 
         Token* tokenFreelist;
-        uint versionNumber;
-        const(char)[] vendor;
     }
 
   nothrow:
@@ -105,13 +114,12 @@ class Lexer
      *  doDocComment = handle documentation comments
      *  commentToken = comments become TOK.comment's
      *  errorSink = where error messages go, must not be null
-     *  vendor = name of the vendor
-     *  versionNumber = version of the caller
+     *  compileEnv = version, vendor, date, time, etc.
      */
     this(const(char)* filename, const(char)* base, size_t begoffset,
         size_t endoffset, bool doDocComment, bool commentToken,
         ErrorSink errorSink,
-        const(char)[] vendor = "DLF", uint versionNumber = 1) pure scope
+        const CompileEnv* compileEnv) pure scope
     {
         scanloc = Loc(filename, 1, 1);
         // debug printf("Lexer::Lexer(%p)\n", base);
@@ -128,8 +136,13 @@ class Lexer
         this.lastDocLine = 0;
         this.eSink = errorSink;
         assert(errorSink);
-        this.versionNumber = versionNumber;
-        this.vendor = vendor;
+        if (compileEnv)
+            this.compileEnv = *compileEnv;
+        else
+        {
+            this.compileEnv.versionNumber = 1;
+            this.compileEnv.vendor = "DLF";
+        }
         //initKeywords();
         /* If first line starts with '#!', ignore the line
          */
@@ -169,10 +182,10 @@ class Lexer
      */
     this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset,
         bool doDocComment, bool commentToken, bool whitespaceToken,
-        ErrorSink errorSink
+        ErrorSink errorSink, const CompileEnv* compileEnv = null
         )
     {
-        this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink);
+        this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink, compileEnv);
         this.whitespaceToken = whitespaceToken;
     }
 
@@ -380,6 +393,15 @@ class Lexer
                     }
                 }
                 continue; // skip white space
+
+            case '\\':
+                if (Ccompile && (p[1] == '\r' || p[1] == '\n'))
+                {
+                    ++p; // ignore \ followed by new line, like VC does
+                    continue;
+                }
+                goto default;
+
             case '0':
                 if (!isZeroSecond(p[1]))        // if numeric literal does not continue
                 {
@@ -571,36 +593,26 @@ class Lexer
 
                     else if (*t.ptr == '_') // if special identifier token
                     {
-                        // Lazy initialization
-                        TimeStampInfo.initialize(t.loc, eSink);
-
-                        if (id == Id.DATE)
+                        void toToken(const(char)[] s)
                         {
-                            t.ustring = TimeStampInfo.date.ptr;
-                            goto Lstr;
+                            t.value = TOK.string_;
+                            t.ustring = s.ptr;
+                            t.len = cast(uint)s.length;
+                            t.postfix = 0;
                         }
+
+                        if (id == Id.DATE)
+                            toToken(compileEnv.date);
                         else if (id == Id.TIME)
-                        {
-                            t.ustring = TimeStampInfo.time.ptr;
-                            goto Lstr;
-                        }
+                            toToken(compileEnv.time);
                         else if (id == Id.VENDOR)
-                        {
-                            t.ustring = vendor.xarraydup.ptr;
-                            goto Lstr;
-                        }
+                            toToken(compileEnv.vendor);
                         else if (id == Id.TIMESTAMP)
-                        {
-                            t.ustring = TimeStampInfo.timestamp.ptr;
-                        Lstr:
-                            t.value = TOK.string_;
-                            t.postfix = 0;
-                            t.len = cast(uint)strlen(t.ustring);
-                        }
+                            toToken(compileEnv.timestamp);
                         else if (id == Id.VERSIONX)
                         {
                             t.value = TOK.int64Literal;
-                            t.unsvalue = versionNumber;
+                            t.unsvalue = compileEnv.versionNumber;
                         }
                         else if (id == Id.EOFX)
                         {
@@ -2570,6 +2582,14 @@ class Lexer
         TOK result;
         bool isOutOfRange = false;
         t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, isOutOfRange) : CTFloat.zero);
+
+        bool imaginary = false;
+        if (*p == 'i' && Ccompile)
+        {
+            ++p;
+            imaginary = true;
+        }
+
         switch (*p)
         {
         case 'F':
@@ -2595,11 +2615,17 @@ class Lexer
             result = TOK.float80Literal;
             break;
         }
+
         if ((*p == 'i' || *p == 'I') && !Ccompile)
         {
             if (*p == 'I')
                 error("use 'i' suffix instead of 'I'");
             p++;
+            imaginary = true;
+        }
+
+        if (imaginary)
+        {
             switch (result)
             {
             case TOK.float32Literal:
@@ -3033,7 +3059,10 @@ class Lexer
         auto dc = (lineComment && anyToken) ? &t.lineComment : &t.blockComment;
         // Combine with previous doc comment, if any
         if (*dc)
-            *dc = combineComments(*dc, buf[], newParagraph).toDString();
+        {
+            auto p = combineComments(*dc, buf[], newParagraph);
+            *dc = p ? p[0 .. strlen(p)] : null;
+        }
         else
             *dc = buf.extractSlice(true);
     }
@@ -3081,42 +3110,6 @@ class Lexer
 
 private:
 
-/// Support for `__DATE__`, `__TIME__`, and `__TIMESTAMP__`
-private struct TimeStampInfo
-{
-    private __gshared bool initdone = false;
-
-    // Note: Those properties need to be guarded by a call to `init`
-    // The API isn't safe, and quite brittle, but it was left this way
-    // over performance concerns.
-    // This is currently only called once, from the lexer.
-    __gshared char[11 + 1] date;
-    __gshared char[8 + 1] time;
-    __gshared char[24 + 1] timestamp;
-
-    public static void initialize(const ref Loc loc, ErrorSink eSink) nothrow
-    {
-        if (initdone)
-            return;
-
-        initdone = true;
-        time_t ct;
-        // https://issues.dlang.org/show_bug.cgi?id=20444
-        if (auto p = getenv("SOURCE_DATE_EPOCH"))
-        {
-            if (!ct.parseDigits(p.toDString()))
-                eSink.error(loc, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p);
-        }
-        else
-            .time(&ct);
-        const p = ctime(&ct);
-        assert(p);
-        snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20);
-        snprintf(&time[0], time.length, "%.8s", p + 11);
-        snprintf(&timestamp[0], timestamp.length, "%.24s", p);
-    }
-}
-
 private enum LS = 0x2028;       // UTF line separator
 private enum PS = 0x2029;       // UTF paragraph separator
 
@@ -3366,7 +3359,7 @@ unittest
      */
     string text = "int"; // We rely on the implicit null-terminator
     ErrorSink errorSink = new ErrorSinkStderr;
-    scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink);
+    scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink, null);
     TOK tok;
     tok = lex1.nextToken();
     //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOK.int32);
@@ -3402,7 +3395,7 @@ unittest
 
     foreach (testcase; testcases)
     {
-        scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink);
+        scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink, null);
         TOK tok = lex2.nextToken();
         size_t iterations = 1;
         while ((tok != TOK.endOfFile) && (iterations++ < testcase.length))
index 5939db5ba854e4c9d60e8dcac189b894e3a31674..badc579554731bcdae95c2791adc4274966d51e9 100644 (file)
@@ -313,6 +313,7 @@ int mutabilityOfType(bool isref, Type t)
  */
 enum DotExpFlag
 {
+    none    = 0,
     gag     = 1,    // don't report "not a property" error and just return null
     noDeref = 2,    // the use of the expression will not attempt a dereference
     noAliasThis = 4, // don't do 'alias this' resolution
@@ -2044,9 +2045,11 @@ extern (C++) abstract class Type : ASTNode
                 t = t.addMod(this.mod);
             return t;
         }
-        if (auto fd = s.isFuncDeclaration())
+        Dsymbol callable = s.isFuncDeclaration();
+        callable = callable ? callable : s.isTemplateDeclaration();
+        if (callable)
         {
-            fd = resolveFuncCall(Loc.initial, null, fd, null, this, ArgumentList(), FuncResolveFlag.quiet);
+            auto fd = resolveFuncCall(Loc.initial, null, callable, null, this, ArgumentList(), FuncResolveFlag.quiet);
             if (!fd || fd.errors || !fd.functionSemantic())
                 return Type.terror;
 
@@ -2065,24 +2068,17 @@ extern (C++) abstract class Type : ASTNode
         {
             return ed.type;
         }
-        if (auto td = s.isTemplateDeclaration())
-        {
-            assert(td._scope);
-            auto fd = resolveFuncCall(Loc.initial, null, td, null, this, ArgumentList(), FuncResolveFlag.quiet);
-            if (!fd || fd.errors || !fd.functionSemantic())
-                return Type.terror;
-
-            auto t = fd.type.nextOf();
-            if (!t)
-                return Type.terror;
-            t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod);
-            return t;
-        }
 
         //printf("%s\n", s.kind());
         return null;
     }
 
+    /**
+     * Check whether this type has endless `alias this` recursion.
+     * Returns:
+     *   `true` if this type has an `alias this` that can be implicitly
+     *    converted back to this type itself.
+     */
     extern (D) final bool checkAliasThisRec()
     {
         Type tb = toBasetype();
@@ -2645,6 +2641,8 @@ extern (C++) abstract class Type : ASTNode
 
         if (t.isimaginary() || t.iscomplex())
         {
+            if (sc.flags & SCOPE.Cfile)
+                return true;            // complex/imaginary not deprecated in C code
             Type rt;
             switch (t.ty)
             {
@@ -4270,7 +4268,7 @@ extern (C++) final class TypeFunction : TypeNext
         super(Tfunction, treturn);
         //if (!treturn) *(char*)0=0;
         //    assert(treturn);
-        assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.typesafe);
+        assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.max);
         this.parameterList = pl;
         this.linkage = linkage;
 
@@ -6513,6 +6511,7 @@ extern (C++) final class TypeTag : Type
 {
     Loc loc;                /// location of declaration
     TOK tok;                /// TOK.struct_, TOK.union_, TOK.enum_
+    structalign_t packalign; /// alignment of struct/union fields
     Identifier id;          /// tag name identifier
     Type base;              /// base type for enums otherwise null
     Dsymbols* members;      /// members of struct, null if none
@@ -6522,13 +6521,14 @@ extern (C++) final class TypeTag : Type
                             ///   struct S { int a; } s1, *s2;
     MOD mod;                /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment)
 
-    extern (D) this(const ref Loc loc, TOK tok, Identifier id, Type base, Dsymbols* members)
+    extern (D) this(const ref Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members)
     {
         //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this);
         super(Ttag);
         this.loc = loc;
         this.tok = tok;
         this.id = id;
+        this.packalign = packalign;
         this.base = base;
         this.members = members;
         this.mod = 0;
@@ -7252,7 +7252,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
                 s ~= "pure ";
             if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe())
                 s ~= "@safe ";
-            if (!f.isNogc && sc.func.setGC())
+            if (!f.isNogc && sc.func.setGC(arg.loc, null))
                 s ~= "nogc ";
             if (s)
             {
@@ -7579,3 +7579,113 @@ TypeVector toBooleanVector(TypeVector tv)
 
     return new TypeVector(new TypeSArray(telem, tsa.dim));
 }
+
+/*************************************************
+ * Dispatch to function based on static type of Type.
+ */
+mixin template VisitType(Result)
+{
+    Result VisitType(Type t)
+    {
+        final switch (t.ty)
+        {
+            case TY.Tvoid:
+            case TY.Tint8:
+            case TY.Tuns8:
+            case TY.Tint16:
+            case TY.Tuns16:
+            case TY.Tint32:
+            case TY.Tuns32:
+            case TY.Tint64:
+            case TY.Tuns64:
+            case TY.Tfloat32:
+            case TY.Tfloat64:
+            case TY.Tfloat80:
+            case TY.Timaginary32:
+            case TY.Timaginary64:
+            case TY.Timaginary80:
+            case TY.Tcomplex32:
+            case TY.Tcomplex64:
+            case TY.Tcomplex80:
+            case TY.Tbool:
+            case TY.Tchar:
+            case TY.Twchar:
+            case TY.Tdchar:
+            case TY.Tint128:
+            case TY.Tuns128:    mixin(visitTYCase("Basic"));
+            case TY.Tarray:     mixin(visitTYCase("DArray"));
+            case TY.Tsarray:    mixin(visitTYCase("SArray"));
+            case TY.Taarray:    mixin(visitTYCase("AArray"));
+            case TY.Tpointer:   mixin(visitTYCase("Pointer"));
+            case TY.Treference: mixin(visitTYCase("Reference"));
+            case TY.Tfunction:  mixin(visitTYCase("Function"));
+            case TY.Tident:     mixin(visitTYCase("Identifier"));
+            case TY.Tclass:     mixin(visitTYCase("Class"));
+            case TY.Tstruct:    mixin(visitTYCase("Struct"));
+            case TY.Tenum:      mixin(visitTYCase("Enum"));
+            case TY.Tdelegate:  mixin(visitTYCase("Delegate"));
+            case TY.Terror:     mixin(visitTYCase("Error"));
+            case TY.Tinstance:  mixin(visitTYCase("Instance"));
+            case TY.Ttypeof:    mixin(visitTYCase("Typeof"));
+            case TY.Ttuple:     mixin(visitTYCase("Tuple"));
+            case TY.Tslice:     mixin(visitTYCase("Slice"));
+            case TY.Treturn:    mixin(visitTYCase("Return"));
+            case TY.Tnull:      mixin(visitTYCase("Null"));
+            case TY.Tvector:    mixin(visitTYCase("Vector"));
+            case TY.Ttraits:    mixin(visitTYCase("Traits"));
+            case TY.Tmixin:     mixin(visitTYCase("Mixin"));
+            case TY.Tnoreturn:  mixin(visitTYCase("Noreturn"));
+            case TY.Ttag:       mixin(visitTYCase("Tag"));
+            case TY.Tnone:      assert(0);
+        }
+    }
+}
+
+/****************************************
+ * CTFE-only helper function for VisitInitializer.
+ * Params:
+ *      handler = string for the name of the visit handler
+ * Returns: boilerplate code for a case
+ */
+pure string visitTYCase(string handler)
+{
+    if (__ctfe)
+    {
+        return
+            "
+            enum isVoid = is(Result == void);
+            auto tx = t.isType"~handler~"();
+            static if (__traits(compiles, visit"~handler~"(tx)))
+            {
+                static if (isVoid)
+                {
+                    visit"~handler~"(tx);
+                    return;
+                }
+                else
+                {
+                    if (Result r = visit"~handler~"(tx))
+                        return r;
+                    return Result.init;
+                }
+            }
+            else static if (__traits(compiles, visitDefaultCase(t)))
+            {
+                static if (isVoid)
+                {
+                    visitDefaultCase(tx);
+                    return;
+                }
+                else
+                {
+                    if (Result r = visitDefaultCase(t))
+                        return r;
+                    return Result.init;
+                }
+            }
+            else
+                static assert(0, "~handler~");
+            ";
+    }
+    assert(0);
+}
index 201f168527cb71c92c9a2686e91c9a7c934abc97..a0f3e60861be6168fa3fcf51da01062697370e49 100644 (file)
@@ -83,7 +83,7 @@ public:
             err = true;
             return true;
         }
-        if (f.setGC())
+        if (f.setGC(e.loc, format))
         {
             e.error(format, f.kind(), f.toPrettyChars());
             err = true;
@@ -135,7 +135,7 @@ public:
 
     override void visit(NewExp e)
     {
-        if (e.member && !e.member.isNogc() && f.setGC())
+        if (e.member && !e.member.isNogc() && f.setGC(e.loc, null))
         {
             // @nogc-ness is already checked in NewExp::semantic
             return;
@@ -195,7 +195,7 @@ public:
             err = true;
             return;
         }
-        if (f.setGC())
+        if (f.setGC(e.loc, null))
         {
             err = true;
             return;
index 9cff76b84aa2fe8930af04ebd20cf165b4dff7a9..89728b6448687af2d099f115e41476f68b0b71ef 100644 (file)
@@ -844,7 +844,7 @@ void toObNodes(ref ObNodes obnodes, Statement s)
             case STMT.Conditional:
             case STMT.While:
             case STMT.Forwarding:
-            case STMT.Compile:
+            case STMT.Mixin:
             case STMT.Peel:
             case STMT.Synchronized:
                 debug printf("s: %s\n", s.toChars());
index 3c80e5e1d0e749375d6f7333b23e289a36cd7b60..d7b90d7635fd8605f8f32576116c7265e4cfe1cf 100644 (file)
@@ -293,6 +293,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 {
                     ie = (*ae.arguments)[0].isIntervalExp();
                 }
+                Type att = null; // first cyclic `alias this` type
                 while (true)
                 {
                     if (ae.e1.op == EXP.error)
@@ -354,7 +355,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         return result;
                     }
                     // Didn't find it. Forward to aliasthis
-                    if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+                    if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
                     {
                         /* Rewrite op(a[arguments]) as:
                          *      op(a.aliasthis[arguments])
@@ -370,13 +371,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
             }
             e.e1 = e.e1.expressionSemantic(sc);
             e.e1 = resolveProperties(sc, e.e1);
-            if (e.e1.op == EXP.error)
-            {
-                return e.e1;
-            }
-            AggregateDeclaration ad = isAggregate(e.e1.type);
-            if (ad)
+            Type att = null; // first cyclic `alias this` type
+            while (1)
             {
+                if (e.e1.op == EXP.error)
+                {
+                    return e.e1;
+                }
+
+                AggregateDeclaration ad = isAggregate(e.e1.type);
+                if (!ad)
+                    break;
+
                 Dsymbol fd = null;
                 /* Rewrite as:
                  *      e1.opUnary!(op)()
@@ -404,18 +410,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     }
                 }
                 // Didn't find it. Forward to aliasthis
-                if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
+                if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
                 {
                     /* Rewrite op(e1) as:
                      *      op(e1.aliasthis)
                      */
                     //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars());
-                    Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
-                    UnaExp ue = cast(UnaExp)e.copy();
-                    ue.e1 = e1;
-                    result = ue.trySemantic(sc);
-                    return result;
+                    e.e1 = resolveAliasThis(sc, e.e1, true);
+                    if (e.e1)
+                        continue;
+                    break;
                 }
+                break;
             }
             return result;
         }
@@ -433,6 +439,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 ie = (*ae.arguments)[0].isIntervalExp();
             }
             Expression result;
+            Type att = null; // first cyclic `alias this` type
             while (true)
             {
                 if (ae.e1.op == EXP.error)
@@ -526,7 +533,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     return result;
                 }
                 // Didn't find it. Forward to aliasthis
-                if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+                if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
                 {
                     //printf("att arr e1 = %s\n", this.e1.type.toChars());
                     /* Rewrite op(a[arguments]) as:
@@ -547,7 +554,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
          * This is mostly the same as UnaryExp::op_overload(), but has
          * a different rewrite.
          */
-        Expression visitCast(CastExp e)
+        Expression visitCast(CastExp e, Type att = null)
         {
             //printf("CastExp::op_overload() (%s)\n", e.toChars());
             Expression result;
@@ -578,7 +585,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     return result;
                 }
                 // Didn't find it. Forward to aliasthis
-                if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
+                if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
                 {
                     /* Rewrite op(e1) as:
                      *      op(e1.aliasthis)
@@ -587,7 +594,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     {
                         result = e.copy();
                         (cast(UnaExp)result).e1 = e1;
-                        result = result.op_overload(sc);
+                        result = visitCast(result.isCastExp(), att);
                         return result;
                     }
                 }
@@ -997,7 +1004,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     return null;
 
                 import dmd.clone : needOpEquals;
-                if (!global.params.fieldwise && !needOpEquals(sd))
+                if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
                 {
                     // Use bitwise equality.
                     auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
@@ -1016,12 +1023,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                  * also compare the parent class's equality. Otherwise, compares
                  * the identity of parent context through void*.
                  */
-                if (e.att1 && t1.equivalent(e.att1)) return null;
-                if (e.att2 && t2.equivalent(e.att2)) return null;
-
                 e = e.copy().isEqualExp();
-                if (!e.att1) e.att1 = t1;
-                if (!e.att2) e.att2 = t2;
                 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
                 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
 
@@ -1029,18 +1031,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 sc2.flags |= SCOPE.noaccesscheck;
                 Expression r = e.expressionSemantic(sc2);
                 sc2.pop();
-
-                /* https://issues.dlang.org/show_bug.cgi?id=15292
-                 * if the rewrite result is same with the original,
-                 * the equality is unresolvable because it has recursive definition.
-                 */
-                if (r.op == e.op &&
-                    r.isEqualExp().e1.type.toBasetype() == t1)
-                {
-                    e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
-                        t1.toChars());
-                    return ErrorExp.get();
-                }
                 return r;
             }
 
@@ -1071,8 +1061,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         auto ex1 = (*tup1.exps)[i];
                         auto ex2 = (*tup2.exps)[i];
                         auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
-                        eeq.att1 = e.att1;
-                        eeq.att2 = e.att2;
 
                         if (!result)
                             result = eeq;
@@ -1114,6 +1102,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 {
                     ie = (*ae.arguments)[0].isIntervalExp();
                 }
+                Type att = null; // first cyclic `alias this` type
                 while (true)
                 {
                     if (ae.e1.op == EXP.error)
@@ -1185,7 +1174,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         return result;
                     }
                     // Didn't find it. Forward to aliasthis
-                    if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+                    if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
                     {
                         /* Rewrite (a[arguments] op= e2) as:
                          *      a.aliasthis[arguments] op= e2
index b5d32b2932d95115ab0a55d0ee498f5e57964cf7..61c385fc06103a7efe37681029dedcf8cd0dd152 100644 (file)
@@ -371,7 +371,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
     {
         if (e.stageflags & stageOptimize)
             return;
-        int old = e.stageflags;
+        const old = e.stageflags;
         e.stageflags |= stageOptimize;
         if (e.elements)
         {
index 36a76f50da20590abc9560533b1901b1258ad00b..68a25064fc0db6c8f674163a572751de9f160b06 100644 (file)
@@ -15,14 +15,13 @@ module dmd.parse;
 
 import core.stdc.stdio;
 import core.stdc.string;
+
 import dmd.astenums;
 import dmd.errorsink;
-import dmd.globals;
 import dmd.id;
 import dmd.identifier;
 import dmd.lexer;
 import dmd.location;
-import dmd.errors;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
@@ -30,6 +29,8 @@ import dmd.root.rootobject;
 import dmd.root.string;
 import dmd.tokens;
 
+alias CompileEnv = dmd.lexer.CompileEnv;
+
 /***********************************************************
  */
 class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
@@ -45,49 +46,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         Loc endloc; // set to location of last right curly
         int inBrackets; // inside [] of array index or slice
         Loc lookingForElse; // location of lonely if looking for an else
+        bool doUnittests; // parse unittest blocks
     }
 
+    bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed
+
     /*********************
      * Use this constructor for string mixins.
      * Input:
-     *      loc     location in source file of mixin
+     *      loc = location in source file of mixin
      */
     extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment,
-        ErrorSink errorSink) scope
+        ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope
     {
-        super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false,
-                errorSink,
-                global.vendor, global.versionNumber());
-
-        //printf("Parser::Parser()\n");
+        //printf("Parser::Parser()1 %d\n", doUnittests);
+        this(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
         scanloc = loc;
-
-        if (!writeMixin(input, scanloc) && loc.filename)
-        {
-            /* Create a pseudo-filename for the mixin string, as it may not even exist
-             * in the source file.
-             */
-            auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1;
-            char* filename = cast(char*)mem.xmalloc(len);
-            snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
-            scanloc.filename = filename;
-        }
-
-        mod = _module;
-        linkage = LINK.d;
-        //nextToken();              // start up the scanner
     }
 
-    extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink) scope
+    /**************************************************
+     * Main Parser constructor.
+     */
+    extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink,
+        const CompileEnv* compileEnv, const bool doUnittests) scope
     {
         super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false,
               errorSink,
-              global.vendor, global.versionNumber());
+              compileEnv);
 
-        //printf("Parser::Parser()\n");
-        mod = _module;
-        linkage = LINK.d;
-        //nextToken();              // start up the scanner
+        //printf("Parser::Parser()2 %d\n", doUnittests);
+        this.mod = _module;
+        this.linkage = LINK.d;
+        this.doUnittests = doUnittests;
     }
 
     /++
@@ -335,6 +325,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             linkage = linksave;
 
             Loc startloc;
+            Loc scdLoc;
 
             switch (token.value)
             {
@@ -381,7 +372,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                             nextToken();
                             auto exps = parseArguments();
                             check(TOK.semicolon);
-                            s = new AST.CompileDeclaration(loc, exps);
+                            s = new AST.MixinDeclaration(loc, exps);
                             break;
                         }
                     case TOK.template_:
@@ -497,7 +488,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                  * template instantiations in these unittests as candidates for
                  * further codegen culling.
                  */
-                if (mod.isRoot() && (global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput))
+                // The isRoot check is here because it can change after parsing begins (see dmodule.d)
+                if (doUnittests && mod.isRoot())
                 {
                     linkage = LINK.d; // unittests have D linkage
                     s = parseUnitTest(pAttrs);
@@ -696,6 +688,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 }
             Lstc:
                 pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
+                scdLoc = token.loc;
                 nextToken();
 
             Lautodecl:
@@ -748,7 +741,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 auto stc2 = getStorageClass!AST(pAttrs);
                 if (stc2 != STC.undefined_)
                 {
-                    s = new AST.StorageClassDeclaration(stc2, a);
+                    s = new AST.StorageClassDeclaration(scdLoc, stc2, a);
                 }
                 if (pAttrs.udas)
                 {
@@ -1219,19 +1212,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             return orig | added;
         }
 
-        const Redundant = (STC.const_ | STC.scope_ |
-                           (global.params.previewIn ? STC.ref_ : 0));
+        const Redundant = (STC.const_ | STC.scope_ | STC.ref_);
         orig |= added;
 
         if ((orig & STC.in_) && (added & Redundant))
         {
             if (added & STC.const_)
                 error("attribute `const` is redundant with previously-applied `in`");
-            else if (global.params.previewIn)
+            else if (compileEnv.previewIn)
             {
                 error("attribute `%s` is redundant with previously-applied `in`",
                       (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
             }
+            else if (added & STC.ref_)
+                deprecation("using `in ref` is deprecated, use `-preview=in` and `in` instead");
             else
                 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
             return orig;
@@ -1241,13 +1235,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         {
             if (orig & STC.const_)
                 error("attribute `in` cannot be added after `const`: remove `const`");
-            else if (global.params.previewIn)
+            else if (compileEnv.previewIn)
             {
                 // Windows `printf` does not support `%1$s`
                 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
                 error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`",
                       stc_str, stc_str);
             }
+            else if (orig & STC.ref_)
+                deprecation("using `ref in` is deprecated, use `-preview=in` and `in` instead");
             else
                 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
             return orig;
@@ -1302,12 +1298,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             return 0;
         }
 
+        AST.Expression templateArgToExp(RootObject o, const ref Loc loc)
+        {
+            switch (o.dyncast)
+            {
+                case DYNCAST.expression:
+                    return cast(AST.Expression) o;
+                case DYNCAST.type:
+                    return new AST.TypeExp(loc, cast(AST.Type)o);
+                default:
+                    assert(0);
+            }
+        }
+
         if (token.value == TOK.leftParenthesis)
         {
             // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
             if (peekNext() == TOK.rightParenthesis)
                 error("empty attribute list is not allowed");
-            udas = AST.UserAttributeDeclaration.concat(udas, parseArguments());
+
+            if (udas is null)
+                udas = new AST.Expressions();
+            auto args = parseTemplateArgumentList();
+            foreach (arg; *args)
+                udas.push(templateArgToExp(arg, token.loc));
+            return 0;
+        }
+
+        if (auto o = parseTemplateSingleArgument())
+        {
+            if (udas is null)
+                udas = new AST.Expressions();
+            udas.push(templateArgToExp(o, token.loc));
             return 0;
         }
 
@@ -1764,7 +1786,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         else
         {
             // ident!template_argument
-            tiargs = parseTemplateSingleArgument();
+            RootObject o = parseTemplateSingleArgument();
+            if (!o)
+            {
+                error("template argument expected following `!`");
+            }
+            else
+            {
+                tiargs = new AST.Objects();
+                tiargs.push(o);
+            }
         }
         if (token.value == TOK.not)
         {
@@ -1830,11 +1861,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
      *      foo!arg
      * Input:
      *      current token is the arg
+     * Returns: An AST.Type, AST.Expression, or `null` on error
      */
-    private AST.Objects* parseTemplateSingleArgument()
+    private RootObject parseTemplateSingleArgument()
     {
         //printf("parseTemplateSingleArgument()\n");
-        auto tiargs = new AST.Objects();
         AST.Type ta;
         switch (token.value)
         {
@@ -1942,9 +1973,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             ta = AST.Type.tdchar;
             goto LabelX;
         LabelX:
-            tiargs.push(ta);
             nextToken();
-            break;
+            return ta;
 
         case TOK.int32Literal:
         case TOK.uns32Literal:
@@ -1974,15 +2004,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         case TOK.this_:
             {
                 // Template argument is an expression
-                AST.Expression ea = parsePrimaryExp();
-                tiargs.push(ea);
-                break;
+                return parsePrimaryExp();
             }
         default:
-            error("template argument expected following `!`");
-            break;
+            return null;
         }
-        return tiargs;
     }
 
     /**********************************
@@ -2694,7 +2720,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         /** Extract unittest body as a string. Must be done eagerly since memory
          will be released by the lexer before doc gen. */
         char* docline = null;
-        if (global.params.ddoc.doOutput && endPtr > begPtr)
+        if (compileEnv.ddocOutput && endPtr > begPtr)
         {
             /* Remove trailing whitespaces */
             for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
@@ -2849,8 +2875,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                         // Don't call nextToken again.
                     }
                 case TOK.in_:
-                    if (global.params.vin)
-                        message(scanloc, "Usage of 'in' on parameter");
+                    if (transitionIn)
+                        eSink.message(scanloc, "Usage of 'in' on parameter");
                     stc = STC.in_;
                     goto L2;
 
@@ -4610,8 +4636,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 AST.Dsymbol s;
                 if (width)
                 {
-                    if (!global.params.bitfields)
-                        error("use -preview=bitfields for bitfield support");
                     if (_init)
                         error("initializer not allowed for bit-field declaration");
                     if (storage_class)
@@ -5144,7 +5168,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         case TOK.goesTo:
             if (requireDo)
                 error("missing `do { ... }` after `in` or `out`");
-            if (!global.params.shortenedMethods)
+            if (!compileEnv.shortenedMethods)
                 error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
             const returnloc = token.loc;
             nextToken();
@@ -5156,7 +5180,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         case TOK.leftCurly:
             if (requireDo)
                 error("missing `do { ... }` after `in` or `out`");
-            f.fbody = parseStatement(ParseStatementFlags.semi);
+            f.fbody = parseStatement(0);
             f.endloc = endloc;
             break;
 
@@ -5801,7 +5825,11 @@ LagainStc:
                     if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon)
                         error("found `%s` when expecting `;` following statement", token.toChars());
                     else
-                        check(TOK.semicolon, "statement");
+                    {
+                        if (token.value != TOK.semicolon)
+                            error("found `%s` when expecting `;` following statement `%s` on line %s", token.toChars(), exp.toChars(), exp.loc.toChars());
+                        nextToken();
+                    }
                 }
                 s = new AST.ExpStatement(loc, exp);
                 break;
@@ -5959,7 +5987,7 @@ LagainStc:
                     if (e.op == EXP.mixin_)
                     {
                         AST.MixinExp cpe = cast(AST.MixinExp)e;
-                        s = new AST.CompileStatement(loc, cpe.exps);
+                        s = new AST.MixinStatement(loc, cpe.exps);
                     }
                     else
                     {
@@ -5992,7 +6020,7 @@ LagainStc:
                 auto statements = new AST.Statements();
                 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
                 {
-                    statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+                    statements.push(parseStatement(ParseStatementFlags.curlyScope));
                 }
                 if (endPtr)
                     *endPtr = token.ptr;
@@ -6025,10 +6053,7 @@ LagainStc:
         case TOK.semicolon:
             if (!(flags & ParseStatementFlags.semiOk))
             {
-                if (flags & ParseStatementFlags.semi)
-                    deprecation("use `{ }` for an empty statement, not `;`");
-                else
-                    error("use `{ }` for an empty statement, not `;`");
+                error("use `{ }` for an empty statement, not `;`");
             }
             nextToken();
             s = new AST.ExpStatement(loc, cast(AST.Expression)null);
@@ -6245,7 +6270,7 @@ LagainStc:
                     _body = null;
                 }
                 else
-                    _body = parseStatement(ParseStatementFlags.semi);
+                    _body = parseStatement(0);
                 s = new AST.PragmaStatement(loc, ident, args, _body);
                 break;
             }
@@ -6298,7 +6323,7 @@ LagainStc:
                     auto statements = new AST.Statements();
                     while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
                     {
-                        auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
+                        auto cur = parseStatement(ParseStatementFlags.curlyScope);
                         statements.push(cur);
 
                         // https://issues.dlang.org/show_bug.cgi?id=21739
@@ -6313,7 +6338,7 @@ LagainStc:
                 }
                 else
                 {
-                    s = parseStatement(ParseStatementFlags.semi);
+                    s = parseStatement(0);
                 }
                 s = new AST.ScopeStatement(loc, s, token.loc);
 
@@ -6342,12 +6367,12 @@ LagainStc:
                     auto statements = new AST.Statements();
                     while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
                     {
-                        statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+                        statements.push(parseStatement(ParseStatementFlags.curlyScope));
                     }
                     s = new AST.CompoundStatement(loc, statements);
                 }
                 else
-                    s = parseStatement(ParseStatementFlags.semi);
+                    s = parseStatement(0);
                 s = new AST.ScopeStatement(loc, s, token.loc);
                 s = new AST.DefaultStatement(loc, s);
                 break;
@@ -6891,6 +6916,15 @@ LagainStc:
 
     /********************
      * Parse inline assembler block.
+     * Enters with token on the `asm`.
+     * https://dlang.org/spec/iasm.html
+     *
+     * AsmStatement:
+     *   asm FunctionAttributes(opt) { AsmInstructionListopt }
+     * AsmInstructionList:
+     *   AsmInstruction ;
+     *   AsmInstruction ; AsmInstruction
+     *
      * Returns:
      *   inline assembler block as a Statement
      */
@@ -6905,7 +6939,7 @@ LagainStc:
         Loc labelloc;
 
         nextToken();
-        StorageClass stc = parsePostfix(STC.undefined_, null);
+        StorageClass stc = parsePostfix(STC.undefined_, null);  // optional FunctionAttributes
         if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
             error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
 
@@ -9181,7 +9215,7 @@ LagainStc:
         void checkRequiredParens()
         {
             if (e.op == EXP.question && !e.parens)
-                dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
+                eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
                     e.toChars(), Token.toChars(token.value));
         }
 
@@ -9498,7 +9532,6 @@ immutable PREC[EXP.max + 1] precedence =
     EXP.error : PREC.expr,
     EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type
 
-    EXP.typeof_ : PREC.primary,
     EXP.mixin_ : PREC.primary,
 
     EXP.import_ : PREC.primary,
@@ -9538,7 +9571,6 @@ immutable PREC[EXP.max + 1] precedence =
     EXP.remove : PREC.primary,
     EXP.tuple : PREC.primary,
     EXP.traits : PREC.primary,
-    EXP.default_ : PREC.primary,
     EXP.overloadSet : PREC.primary,
     EXP.void_ : PREC.primary,
     EXP.vectorArray : PREC.primary,
@@ -9637,7 +9669,6 @@ immutable PREC[EXP.max + 1] precedence =
 
 enum ParseStatementFlags : int
 {
-    semi          = 1,        // empty ';' statements are allowed, but deprecated
     scope_        = 2,        // start a new scope
     curly         = 4,        // { } statement is required
     curlyScope    = 8,        // { } starts a new scope
@@ -9708,52 +9739,3 @@ private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
     }
     return stc;
 }
-
-/**************************************
- * dump mixin expansion to file for better debugging
- */
-private bool writeMixin(const(char)[] s, ref Loc loc)
-{
-    if (!global.params.mixinOut.doOutput)
-        return false;
-
-    OutBuffer* ob = global.params.mixinOut.buffer;
-
-    ob.writestring("// expansion at ");
-    ob.writestring(loc.toChars());
-    ob.writenl();
-
-    global.params.mixinOut.bufferLines++;
-
-    loc = Loc(global.params.mixinOut.name.ptr, global.params.mixinOut.bufferLines + 1, loc.charnum);
-
-    // write by line to create consistent line endings
-    size_t lastpos = 0;
-    for (size_t i = 0; i < s.length; ++i)
-    {
-        // detect LF and CRLF
-        const c = s[i];
-        if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
-        {
-            ob.writestring(s[lastpos .. i]);
-            ob.writenl();
-            global.params.mixinOut.bufferLines++;
-            if (c == '\r')
-                ++i;
-            lastpos = i + 1;
-        }
-    }
-
-    if(lastpos < s.length)
-        ob.writestring(s[lastpos .. $]);
-
-    if (s.length == 0 || s[$-1] != '\n')
-    {
-        ob.writenl(); // ensure empty line after expansion
-        global.params.mixinOut.bufferLines++;
-    }
-    ob.writenl();
-    global.params.mixinOut.bufferLines++;
-
-    return true;
-}
index 387b28c153292af0d1bda6d23c7710ada43c614c..a4a9434334e06c4d7c647fb843d73670eab274f6 100644 (file)
@@ -66,7 +66,7 @@ public:
     void visit(AST.SharedStaticDtorDeclaration s) { visit(cast(AST.StaticDtorDeclaration)s); }
 
     // AttribDeclarations
-    void visit(AST.CompileDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+    void visit(AST.MixinDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
     void visit(AST.UserAttributeDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
     void visit(AST.LinkDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
     void visit(AST.AnonDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
@@ -99,7 +99,7 @@ public:
     void visit(AST.ReturnStatement s) { visit(cast(AST.Statement)s); }
     void visit(AST.LabelStatement s) { visit(cast(AST.Statement)s); }
     void visit(AST.StaticAssertStatement s) { visit(cast(AST.Statement)s); }
-    void visit(AST.CompileStatement s) { visit(cast(AST.Statement)s); }
+    void visit(AST.MixinStatement s) { visit(cast(AST.Statement)s); }
     void visit(AST.WhileStatement s) { visit(cast(AST.Statement)s); }
     void visit(AST.ForStatement s) { visit(cast(AST.Statement)s); }
     void visit(AST.DoStatement s) { visit(cast(AST.Statement)s); }
index d85105d6f202a155e2039a72dcfb1d53f4f58a2d..8c0109524f7d449bafc518fc71e8e79957183609 100644 (file)
@@ -219,6 +219,25 @@ extern (C++) final class PrintASTVisitor : Visitor
         printAST(e.value, indent + 2);
     }
 
+    override void visit(ArrayLiteralExp e)
+    {
+        visit(cast(Expression)e);
+        printIndent(indent + 2);
+        printf(".basis : %s\n", e.basis ? e.basis.toChars() : "");
+        if (e.elements)
+        {
+            printIndent(indent + 2);
+            printf("[");
+            foreach (i, element; (*e.elements)[])
+            {
+                if (i)
+                    printf(", ");
+                printf("%s", element.toChars());
+            }
+            printf("]\n");
+        }
+    }
+
     static void printIndent(int indent)
     {
         foreach (i; 0 .. indent)
index a912e768f0c1fd03dd03bfabf289be026d98096b..33c2f02249e101a57aef9e57e6290308a98577b2 100644 (file)
@@ -467,7 +467,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                         /* Generate identifier for un-named parameter,
                          * because we need it later on.
                          */
-                        fparam.ident = id = Identifier.generateId("_param_", i);
+                        fparam.ident = id = Identifier.generateId("__param_", i);
                         stc |= STC.temp;
                     }
                     Type vtype = fparam.type;
index 60a74cc2812b7ae9e5b0c07b2f89d7da415f6ee5..3f3e7e6377a288f94a7d27e9735f1e4f0a0b790d 100644 (file)
@@ -185,6 +185,7 @@ private bool lambdaHasSideEffect(Expression e, bool assumeImpureCalls = false)
     case EXP.delete_:
     case EXP.new_:
     case EXP.newAnonymousClass:
+    case EXP.loweredAssignExp:
         return true;
     case EXP.call:
         {
index 90728fb6fec5a4695e4ec7fc76d57ee19a4c0c54..3ccf228d1e9c28f326aef1bf7111986cab597e47 100644 (file)
@@ -373,6 +373,7 @@ extern (C++) abstract class Statement : ASTNode
      *    the downcast statement if it can be downcasted, otherwise `null`
      */
     inout(ErrorStatement)       isErrorStatement()       { return stmt == STMT.Error       ? cast(typeof(return))this : null; }
+    inout(PeelStatement)        isPeelStatement()        { return stmt == STMT.Peel        ? cast(typeof(return))this : null; }
     inout(ScopeStatement)       isScopeStatement()       { return stmt == STMT.Scope       ? cast(typeof(return))this : null; }
     inout(ExpStatement)         isExpStatement()         { return stmt == STMT.Exp         ? cast(typeof(return))this : null; }
     inout(CompoundStatement)    isCompoundStatement()    { return stmt == STMT.Compound    ? cast(typeof(return))this : null; }
@@ -388,7 +389,7 @@ extern (C++) abstract class Statement : ASTNode
     inout(GotoCaseStatement)    isGotoCaseStatement()    { return stmt == STMT.GotoCase    ? cast(typeof(return))this : null; }
     inout(BreakStatement)       isBreakStatement()       { return stmt == STMT.Break       ? cast(typeof(return))this : null; }
     inout(DtorExpStatement)     isDtorExpStatement()     { return stmt == STMT.DtorExp     ? cast(typeof(return))this : null; }
-    inout(CompileStatement)     isCompileStatement()     { return stmt == STMT.Compile     ? cast(typeof(return))this : null; }
+    inout(MixinStatement)       isMixinStatement()       { return stmt == STMT.Mixin       ? cast(typeof(return))this : null; }
     inout(ForwardingStatement)  isForwardingStatement()  { return stmt == STMT.Forwarding  ? cast(typeof(return))this : null; }
     inout(DoStatement)          isDoStatement()          { return stmt == STMT.Do          ? cast(typeof(return))this : null; }
     inout(WhileStatement)       isWhileStatement()       { return stmt == STMT.While       ? cast(typeof(return))this : null; }
@@ -406,6 +407,15 @@ extern (C++) abstract class Statement : ASTNode
     inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; }
     inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; }
     inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; }
+    inout(CompoundAsmStatement)  isCompoundAsmStatement()  { return stmt == STMT.CompoundAsm  ? cast(typeof(return))this : null; }
+    inout(PragmaStatement)       isPragmaStatement()       { return stmt == STMT.Pragma       ? cast(typeof(return))this : null; }
+    inout(StaticAssertStatement) isStaticAssertStatement() { return stmt == STMT.StaticAssert ? cast(typeof(return))this : null; }
+    inout(CaseRangeStatement)    isCaseRangeStatement()    { return stmt == STMT.CaseRange    ? cast(typeof(return))this : null; }
+    inout(SynchronizedStatement) isSynchronizedStatement() { return stmt == STMT.Synchronized ? cast(typeof(return))this : null; }
+    inout(AsmStatement)          isAsmStatement()          { return stmt == STMT.Asm          ? cast(typeof(return))this : null; }
+    inout(InlineAsmStatement)    isInlineAsmStatement()    { return stmt == STMT.InlineAsm    ? cast(typeof(return))this : null; }
+    inout(GccAsmStatement)       isGccAsmStatement()       { return stmt == STMT.GccAsm       ? cast(typeof(return))this : null; }
+    inout(ImportStatement)       isImportStatement()       { return stmt == STMT.Import       ? cast(typeof(return))this : null; }
 }
 
 /***********************************************************
@@ -518,7 +528,8 @@ extern (C++) final class DtorExpStatement : ExpStatement
 /***********************************************************
  * https://dlang.org/spec/statement.html#mixin-statement
  */
-extern (C++) final class CompileStatement : Statement
+// Note: was called CompileStatement
+extern (C++) final class MixinStatement : Statement
 {
     Expressions* exps;
 
@@ -531,13 +542,13 @@ extern (C++) final class CompileStatement : Statement
 
     extern (D) this(const ref Loc loc, Expressions* exps)
     {
-        super(loc, STMT.Compile);
+        super(loc, STMT.Mixin);
         this.exps = exps;
     }
 
-    override CompileStatement syntaxCopy()
+    override MixinStatement syntaxCopy()
     {
-        return new CompileStatement(loc, Expression.arraySyntaxCopy(exps));
+        return new MixinStatement(loc, Expression.arraySyntaxCopy(exps));
     }
 
     override void accept(Visitor v)
@@ -2084,3 +2095,107 @@ extern (C++) final class ImportStatement : Statement
         v.visit(this);
     }
 }
+
+
+mixin template VisitStatement(Result)
+{
+    Result VisitStatement(Statement s)
+    {
+        final switch (s.stmt)
+        {
+            case STMT.Error:         mixin(visitStmtCase("Error"));
+            case STMT.Scope:         mixin(visitStmtCase("Scope"));
+            case STMT.Exp:           mixin(visitStmtCase("Exp"));
+            case STMT.Compound:      mixin(visitStmtCase("Compound"));
+            case STMT.Return:        mixin(visitStmtCase("Return"));
+            case STMT.If:            mixin(visitStmtCase("If"));
+            case STMT.Conditional:   mixin(visitStmtCase("Conditional"));
+            case STMT.StaticForeach: mixin(visitStmtCase("StaticForeach"));
+            case STMT.Case:          mixin(visitStmtCase("Case"));
+            case STMT.Default:       mixin(visitStmtCase("Default"));
+            case STMT.Label:         mixin(visitStmtCase("Label"));
+            case STMT.Goto:          mixin(visitStmtCase("Goto"));
+            case STMT.GotoDefault:   mixin(visitStmtCase("GotoDefault"));
+            case STMT.GotoCase:      mixin(visitStmtCase("GotoCase"));
+            case STMT.Break:         mixin(visitStmtCase("Break"));
+            case STMT.DtorExp:       mixin(visitStmtCase("DtorExp"));
+            case STMT.Mixin:         mixin(visitStmtCase("Mixin"));
+            case STMT.Forwarding:    mixin(visitStmtCase("Forwarding"));
+            case STMT.Do:            mixin(visitStmtCase("Do"));
+            case STMT.While:         mixin(visitStmtCase("While"));
+            case STMT.For:           mixin(visitStmtCase("For"));
+            case STMT.Foreach:       mixin(visitStmtCase("Foreach"));
+            case STMT.Switch:        mixin(visitStmtCase("Switch"));
+            case STMT.Continue:      mixin(visitStmtCase("Continue"));
+            case STMT.With:          mixin(visitStmtCase("With"));
+            case STMT.TryCatch:      mixin(visitStmtCase("TryCatch"));
+            case STMT.Throw:         mixin(visitStmtCase("Throw"));
+            case STMT.Debug:         mixin(visitStmtCase("Debug"));
+            case STMT.TryFinally:    mixin(visitStmtCase("TryFinally"));
+            case STMT.ScopeGuard:    mixin(visitStmtCase("ScopeGuard"));
+            case STMT.SwitchError:   mixin(visitStmtCase("SwitchError"));
+            case STMT.UnrolledLoop:  mixin(visitStmtCase("UnrolledLoop"));
+            case STMT.ForeachRange:  mixin(visitStmtCase("ForeachRange"));
+            case STMT.CompoundDeclaration: mixin(visitStmtCase("CompoundDeclaration"));
+            case STMT.Peel:          mixin(visitStmtCase("Peel"));
+            case STMT.CompoundAsm:   mixin(visitStmtCase("CompoundAsm"));
+            case STMT.Pragma:        mixin(visitStmtCase("Pragma"));
+            case STMT.StaticAssert:  mixin(visitStmtCase("StaticAssert"));
+            case STMT.CaseRange:     mixin(visitStmtCase("CaseRange"));
+            case STMT.Synchronized:  mixin(visitStmtCase("Synchronized"));
+            case STMT.Asm:           mixin(visitStmtCase("Asm"));
+            case STMT.InlineAsm:     mixin(visitStmtCase("InlineAsm"));
+            case STMT.GccAsm:        mixin(visitStmtCase("GccAsm"));
+            case STMT.Import:        mixin(visitStmtCase("Import"));
+        }
+    }
+}
+
+/****************************************
+ * CTFE-only helper function for VisitInitializer.
+ * Params:
+ *      handler = string for the name of the visit handler
+ * Returns: boilerplate code for a case
+ */
+pure string visitStmtCase(string handler)
+{
+    if (__ctfe)
+    {
+        return
+            "
+            enum isVoid = is(Result == void);
+            auto sx = s.is"~handler~"Statement();
+            static if (__traits(compiles, visit"~handler~"(sx)))
+            {
+                static if (isVoid)
+                {
+                    visit"~handler~"(sx);
+                    return;
+                }
+                else
+                {
+                    if (Result r = visit"~handler~"(sx))
+                        return r;
+                    return Result.init;
+                }
+            }
+            else static if (__traits(compiles, visitDefaultCase(s)))
+            {
+                static if (isVoid)
+                {
+                    visitDefaultCase(sx);
+                    return;
+                }
+                else
+                {
+                    if (Result r = visitDefaultCase(s))
+                        return r;
+                    return Result.init;
+                }
+            }
+            else
+                static assert(0, "~handler~");
+            ";
+    }
+    assert(0);
+}
index 6d1f85b38d9379e2b234201ff7d13e38d69791f3..b7403b5d8ad1c3fee27d46f3d8dd06ccf0c2ccff 100644 (file)
@@ -65,7 +65,7 @@ enum
     STMTerror,
     STMTpeel,
     STMTexp, STMTdtorExp,
-    STMTcompile,
+    STMTmixin,
     STMTcompound, STMTcompoundDeclaration, STMTcompoundAsm,
     STMTunrolledLoop,
     STMTscope,
@@ -143,7 +143,7 @@ public:
     GotoCaseStatement    *isGotoCaseStatement()    { return stmt == STMTgotoCase    ? (GotoCaseStatement*)this    : NULL; }
     BreakStatement       *isBreakStatement()       { return stmt == STMTbreak       ? (BreakStatement*)this       : NULL; }
     DtorExpStatement     *isDtorExpStatement()     { return stmt == STMTdtorExp     ? (DtorExpStatement*)this     : NULL; }
-    CompileStatement     *isCompileStatement()     { return stmt == STMTcompile     ? (CompileStatement*)this     : NULL; }
+    MixinStatement       *isMixinStatement()       { return stmt == STMTmixin       ? (MixinStatement*)this       : NULL; }
     ForwardingStatement  *isForwardingStatement()  { return stmt == STMTforwarding  ? (ForwardingStatement*)this  : NULL; }
     DoStatement          *isDoStatement()          { return stmt == STMTdo          ? (DoStatement*)this          : NULL; }
     ForStatement         *isForStatement()         { return stmt == STMTfor         ? (ForStatement*)this         : NULL; }
@@ -206,12 +206,12 @@ public:
     void accept(Visitor *v) override { v->visit(this); }
 };
 
-class CompileStatement final : public Statement
+class MixinStatement final : public Statement
 {
 public:
     Expressions *exps;
 
-    CompileStatement *syntaxCopy() override;
+    MixinStatement *syntaxCopy() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
index 694db28b56cd287a40129733890c36910e15f238..f849ce1f69a5572ee5ad986fb336973d2a6e0c2d 100644 (file)
@@ -145,43 +145,35 @@ extern(C++) Statement statementSemantic(Statement s, Scope* sc)
     version (CallbackAPI)
         Compiler.onStatementSemanticStart(s, sc);
 
-    scope v = new StatementSemanticVisitor(sc);
-    s.accept(v);
+    Statement result = statementSemanticVisit(s, sc);
 
     version (CallbackAPI)
         Compiler.onStatementSemanticDone(s, sc);
 
-    return v.result;
+    return result;
 }
 
-package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
+package (dmd)
+Statement statementSemanticVisit(Statement s, Scope* sc)
 {
-    alias visit = Visitor.visit;
-
     Statement result;
-    Scope* sc;
-
-    this(Scope* sc) scope
-    {
-        this.sc = sc;
-    }
 
-    private void setError()
+    void setError()
     {
         result = new ErrorStatement();
     }
 
-    override void visit(Statement s)
+    void visitDefaultCase(Statement s)
     {
         result = s;
     }
 
-    override void visit(ErrorStatement s)
+    void visitError(ErrorStatement s)
     {
         result = s;
     }
 
-    override void visit(PeelStatement s)
+    void visitPeel(PeelStatement s)
     {
         /* "peel" off this wrapper, and don't run semantic()
          * on the result.
@@ -189,7 +181,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = s.s;
     }
 
-    override void visit(ExpStatement s)
+    void visitExp(ExpStatement s)
     {
         /* https://dlang.org/spec/statement.html#expression-statement
          */
@@ -226,12 +218,17 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = s;
     }
 
-    override void visit(CompileStatement cs)
+    void visitDtorExp(DtorExpStatement s)
+    {
+        visitExp(s);
+    }
+
+    void visitMixin(MixinStatement cs)
     {
         /* https://dlang.org/spec/statement.html#mixin-statement
          */
 
-        //printf("CompileStatement::semantic() %s\n", exp.toChars());
+        //printf("MixinStatement::semantic() %s\n", exp.toChars());
         Statements* a = cs.flatten(sc);
         if (!a)
             return;
@@ -239,7 +236,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = s.statementSemantic(sc);
     }
 
-    override void visit(CompoundStatement cs)
+    void visitCompound(CompoundStatement cs)
     {
         //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
         version (none)
@@ -431,7 +428,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = cs;
     }
 
-    override void visit(UnrolledLoopStatement uls)
+    void visitUnrolledLoop(UnrolledLoopStatement uls)
     {
         //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
         Scope* scd = sc.push();
@@ -454,7 +451,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = serror ? serror : uls;
     }
 
-    override void visit(ScopeStatement ss)
+    void visitScope(ScopeStatement ss)
     {
         //printf("ScopeStatement::semantic(sc = %p)\n", sc);
         if (!ss.statement)
@@ -501,7 +498,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ss;
     }
 
-    override void visit(ForwardingStatement ss)
+    void visitForwarding(ForwardingStatement ss)
     {
         assert(ss.sym);
         for (Scope* csc = sc; !ss.sym.parent; csc = csc.enclosing)
@@ -517,7 +514,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ss.statement;
     }
 
-    override void visit(WhileStatement ws)
+    void visitWhile(WhileStatement ws)
     {
         /* Rewrite as a for(;condition;) loop
          * https://dlang.org/spec/statement.html#while-statement
@@ -544,7 +541,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = s;
     }
 
-    override void visit(DoStatement ds)
+    void visitDo(DoStatement ds)
     {
         /* https://dlang.org/spec/statement.html#do-statement
          */
@@ -580,7 +577,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ds;
     }
 
-    override void visit(ForStatement fs)
+    void visitFor(ForStatement fs)
     {
         /* https://dlang.org/spec/statement.html#for-statement
          */
@@ -674,7 +671,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = fs;
     }
 
-    override void visit(ForeachStatement fs)
+    void visitForeach(ForeachStatement fs)
     {
         /* https://dlang.org/spec/statement.html#foreach-statement
          */
@@ -1349,7 +1346,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
                         auto exp = (*exps)[i];
                         version (none)
                         {
-                            printf("[%d] p = %s %s, exp = %s %s\n", i,
+                            printf("[%lu] p = %s %s, exp = %s %s\n", i,
                                 p.type ? p.type.toChars() : "?", p.ident.toChars(),
                                 exp.type.toChars(), exp.toChars());
                         }
@@ -1360,7 +1357,11 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
                         if (ignoreRef) sc &= ~STC.ref_;
                         p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2);
                         if (!exp.implicitConvTo(p.type))
-                            return rangeError();
+                        {
+                            fs.error("cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`",
+                                exp.type.toChars(), p.toChars(), p.type.toChars());
+                            return retError();
+                        }
 
                         auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
                         var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_;
@@ -1395,312 +1396,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
     }
 
-    private static extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde,
-                Type tab, Scope* sc2, Dsymbol sapply)
-    {
-        version (none)
-        {
-            if (global.params.useDIP1000 == FeatureState.enabled)
-            {
-                message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
-            }
-            (cast(FuncExp)flde).fd.tookAddressOf = 1;
-        }
-        else
-        {
-            if (global.params.useDIP1000 == FeatureState.enabled)
-                ++(cast(FuncExp)flde).fd.tookAddressOf;  // allocate a closure unless the opApply() uses 'scope'
-        }
-        assert(tab.ty == Tstruct || tab.ty == Tclass);
-        assert(sapply);
-        /* Call:
-         *  aggr.apply(flde)
-         */
-        Expression ec;
-        ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident);
-        ec = new CallExp(fs.loc, ec, flde);
-        ec = ec.expressionSemantic(sc2);
-        if (ec.op == EXP.error)
-            return null;
-        if (ec.type != Type.tint32)
-        {
-            fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
-            return null;
-        }
-        return ec;
-    }
-
-    private static extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde,
-                                                      Type tab, Scope* sc2)
-    {
-        Expression ec;
-        /* Call:
-         *      aggr(flde)
-         */
-        if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() &&
-            !(cast(DelegateExp)fs.aggr).func.needThis())
-        {
-            // https://issues.dlang.org/show_bug.cgi?id=3560
-            fs.aggr = (cast(DelegateExp)fs.aggr).e1;
-        }
-        ec = new CallExp(fs.loc, fs.aggr, flde);
-        ec = ec.expressionSemantic(sc2);
-        if (ec.op == EXP.error)
-            return null;
-        if (ec.type != Type.tint32)
-        {
-            fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
-            return null;
-        }
-        return ec;
-    }
-
-    private static extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
-                                                   Type tab, Scope* sc2, Type tn, Type tnv)
-    {
-        Expression ec;
-        const dim = fs.parameters.length;
-        const loc = fs.loc;
-        /* Call:
-         *      _aApply(aggr, flde)
-         */
-        static immutable fntab =
-        [
-         "cc", "cw", "cd",
-         "wc", "cc", "wd",
-         "dc", "dw", "dd"
-        ];
-
-        const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
-        char[BUFFER_LEN] fdname;
-        int flag;
-
-        switch (tn.ty)
-        {
-            case Tchar:     flag = 0;   break;
-            case Twchar:    flag = 3;   break;
-            case Tdchar:    flag = 6;   break;
-            default:
-                assert(0);
-        }
-        switch (tnv.ty)
-        {
-            case Tchar:     flag += 0;  break;
-            case Twchar:    flag += 1;  break;
-            case Tdchar:    flag += 2;  break;
-            default:
-                assert(0);
-        }
-        const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
-        int j = snprintf(fdname.ptr, BUFFER_LEN,  "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim);
-        assert(j < BUFFER_LEN);
-
-        FuncDeclaration fdapply;
-        TypeDelegate dgty;
-        auto params = new Parameters();
-        params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null));
-        auto dgparams = new Parameters();
-        dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
-        if (dim == 2)
-            dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
-        dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
-        params.push(new Parameter(0, dgty, null, null, null));
-        fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
-
-        if (tab.isTypeSArray())
-            fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
-        // paint delegate argument to the type runtime expects
-        Expression fexp = flde;
-        if (!dgty.equals(flde.type))
-        {
-            fexp = new CastExp(loc, flde, flde.type);
-            fexp.type = dgty;
-        }
-        ec = new VarExp(Loc.initial, fdapply, false);
-        ec = new CallExp(loc, ec, fs.aggr, fexp);
-        ec.type = Type.tint32; // don't run semantic() on ec
-        return ec;
-    }
-
-    private static extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab)
-    {
-        auto taa = tab.isTypeAArray();
-        Expression ec;
-        const dim = fs.parameters.length;
-        // Check types
-        Parameter p = (*fs.parameters)[0];
-        bool isRef = (p.storageClass & STC.ref_) != 0;
-        Type ta = p.type;
-        if (dim == 2)
-        {
-            Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
-            if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
-            {
-                fs.error("`foreach`: index must be type `%s`, not `%s`",
-                         ti.toChars(), ta.toChars());
-                return null;
-            }
-            p = (*fs.parameters)[1];
-            isRef = (p.storageClass & STC.ref_) != 0;
-            ta = p.type;
-        }
-        Type taav = taa.nextOf();
-        if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
-        {
-            fs.error("`foreach`: value must be type `%s`, not `%s`",
-                     taav.toChars(), ta.toChars());
-            return null;
-        }
-
-        /* Call:
-         *  extern(C) int _aaApply(void*, in size_t, int delegate(void*))
-         *      _aaApply(aggr, keysize, flde)
-         *
-         *  extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
-         *      _aaApply2(aggr, keysize, flde)
-         */
-        __gshared FuncDeclaration* fdapply = [null, null];
-        __gshared TypeDelegate* fldeTy = [null, null];
-        ubyte i = (dim == 2 ? 1 : 0);
-        if (!fdapply[i])
-        {
-            auto params = new Parameters();
-            params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null));
-            params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null));
-            auto dgparams = new Parameters();
-            dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
-            if (dim == 2)
-                dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
-            fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
-            params.push(new Parameter(0, fldeTy[i], null, null, null));
-            fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
-        }
-
-        auto exps = new Expressions();
-        exps.push(fs.aggr);
-        auto keysize = taa.index.size();
-        if (keysize == SIZE_INVALID)
-            return null;
-        assert(keysize < keysize.max - target.ptrsize);
-        keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
-        // paint delegate argument to the type runtime expects
-        Expression fexp = flde;
-        if (!fldeTy[i].equals(flde.type))
-        {
-            fexp = new CastExp(fs.loc, flde, flde.type);
-            fexp.type = fldeTy[i];
-        }
-        exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
-        exps.push(fexp);
-        ec = new VarExp(Loc.initial, fdapply[i], false);
-        ec = new CallExp(fs.loc, ec, exps);
-        ec.type = Type.tint32; // don't run semantic() on ec
-        return ec;
-    }
-
-    private static extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc)
-    {
-        if (!cases.length)
-        {
-            // Easy case, a clean exit from the loop
-            e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
-            return new ExpStatement(loc, e);
-        }
-        // Construct a switch statement around the return value
-        // of the apply function.
-        Statement s;
-        auto a = new Statements();
-
-        // default: break; takes care of cases 0 and 1
-        s = new BreakStatement(Loc.initial, null);
-        s = new DefaultStatement(Loc.initial, s);
-        a.push(s);
-
-        // cases 2...
-        foreach (i, c; *cases)
-        {
-            s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
-            a.push(s);
-        }
-
-        s = new CompoundStatement(loc, a);
-        return new SwitchStatement(loc, e, s, false);
-    }
-    /*************************************
-     * Turn foreach body into the function literal:
-     *  int delegate(ref T param) { body }
-     * Params:
-     *  sc = context
-     *  fs = ForeachStatement
-     *  tfld = type of function literal to be created (type of opApply() function if any), can be null
-     * Returns:
-     *  Function literal created, as an expression
-     *  null if error.
-     */
-    static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
-    {
-        auto params = new Parameters();
-        foreach (i, p; *fs.parameters)
-        {
-            StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_);
-            Identifier id;
-
-            p.type = p.type.typeSemantic(fs.loc, sc);
-            p.type = p.type.addStorageClass(p.storageClass);
-            if (tfld)
-            {
-                Parameter prm = tfld.parameterList[i];
-                //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
-                stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_);
-                if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_))
-                {
-                    if (!(prm.storageClass & STC.ref_))
-                    {
-                        fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars());
-                        return null;
-                    }
-                    goto LcopyArg;
-                }
-                id = p.ident; // argument copy is not need.
-            }
-            else if (p.storageClass & STC.ref_)
-            {
-                // default delegate parameters are marked as ref, then
-                // argument copy is not need.
-                id = p.ident;
-            }
-            else
-            {
-                // Make a copy of the ref argument so it isn't
-                // a reference.
-            LcopyArg:
-                id = Identifier.generateId("__applyArg", cast(int)i);
-
-                Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
-                auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
-                v.storage_class |= STC.temp | (stc & STC.scope_);
-                Statement s = new ExpStatement(fs.loc, v);
-                fs._body = new CompoundStatement(fs.loc, s, fs._body);
-            }
-            params.push(new Parameter(stc, p.type, id, null, null));
-        }
-        // https://issues.dlang.org/show_bug.cgi?id=13840
-        // Throwable nested function inside nothrow function is acceptable.
-        StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
-        auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
-        fs.cases = new Statements();
-        fs.gotos = new ScopeStatements();
-        auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
-        fld.fbody = fs._body;
-        Expression flde = new FuncExp(fs.loc, fld);
-        flde = flde.expressionSemantic(sc);
-        fld.tookAddressOf = 0;
-        if (flde.op == EXP.error)
-            return null;
-        return cast(FuncExp)flde;
-    }
-
-    override void visit(ForeachRangeStatement fs)
+    void visitForeachRange(ForeachRangeStatement fs)
     {
         /* https://dlang.org/spec/statement.html#foreach-range-statement
          */
@@ -1886,7 +1582,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = s.statementSemantic(sc);
     }
 
-    override void visit(IfStatement ifs)
+    void visitIf(IfStatement ifs)
     {
         /* https://dlang.org/spec/statement.html#IfStatement
          */
@@ -1959,6 +1655,20 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         // Save 'root' of two branches (then and else) at the point where it forks
         CtorFlow ctorflow_root = scd.ctorflow.clone();
 
+        /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A`
+         */
+        NotExp notExp;
+        if (ifs.elsebody &&
+            (notExp = ifs.condition.isNotExp()) !is null &&
+            notExp.e1.isVarExp() &&
+            notExp.e1.isVarExp().var.ident == Id.ctfe)
+        {
+            ifs.condition = notExp.e1;
+            auto sbody = ifs.ifbody;
+            ifs.ifbody = ifs.elsebody;
+            ifs.elsebody = sbody;
+        }
+
         /* Detect `if (__ctfe)`
          */
         if (ifs.isIfCtfeBlock())
@@ -1991,7 +1701,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ifs;
     }
 
-    override void visit(ConditionalStatement cs)
+    void visitConditional(ConditionalStatement cs)
     {
         //printf("ConditionalStatement::semantic()\n");
 
@@ -2020,7 +1730,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
     }
 
-    override void visit(PragmaStatement ps)
+    void visitPragma(PragmaStatement ps)
     {
         /* https://dlang.org/spec/statement.html#pragma-statement
          */
@@ -2104,14 +1814,14 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ps._body;
     }
 
-    override void visit(StaticAssertStatement s)
+    void visitStaticAssert(StaticAssertStatement s)
     {
         s.sa.semantic2(sc);
         if (s.sa.errors)
             return setError();
     }
 
-    override void visit(SwitchStatement ss)
+    void visitSwitch(SwitchStatement ss)
     {
         /* https://dlang.org/spec/statement.html#switch-statement
          */
@@ -2288,6 +1998,11 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
             {
                 s = new BreakStatement(ss.loc, null);   // default for C is `default: break;`
             }
+            else if (!sc.needsCodegen())
+            {
+                // something for the interpreter to deal with
+                s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
+            }
             else if (global.params.useSwitchError == CHECKENABLE.on &&
                 global.params.checkAction != CHECKACTION.halt)
             {
@@ -2337,7 +2052,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
 
 
-        if (!ss.condition.type.isString())
+        if (!(ss.condition.type.isString() && sc.needsCodegen()))
         {
             sc.pop();
             result = ss;
@@ -2420,7 +2135,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ss;
     }
 
-    override void visit(CaseStatement cs)
+    void visitCase(CaseStatement cs)
     {
         SwitchStatement sw = sc.sw;
         bool errors = false;
@@ -2566,7 +2281,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = cs;
     }
 
-    override void visit(CaseRangeStatement crs)
+    void visitCaseRange(CaseRangeStatement crs)
     {
         SwitchStatement sw = sc.sw;
         if (sw is null)
@@ -2649,7 +2364,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = s;
     }
 
-    override void visit(DefaultStatement ds)
+    void visitDefault(DefaultStatement ds)
     {
         //printf("DefaultStatement::semantic()\n");
         bool errors = false;
@@ -2693,7 +2408,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ds;
     }
 
-    override void visit(GotoDefaultStatement gds)
+    void visitGotoDefault(GotoDefaultStatement gds)
     {
         /* https://dlang.org/spec/statement.html#goto-statement
          */
@@ -2712,7 +2427,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = gds;
     }
 
-    override void visit(GotoCaseStatement gcs)
+    void visitGotoCase(GotoCaseStatement gcs)
     {
         /* https://dlang.org/spec/statement.html#goto-statement
          */
@@ -2736,7 +2451,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = gcs;
     }
 
-    override void visit(ReturnStatement rs)
+    void visitReturn(ReturnStatement rs)
     {
         /* https://dlang.org/spec/statement.html#return-statement
          */
@@ -3129,7 +2844,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = rs;
     }
 
-    override void visit(BreakStatement bs)
+    void visitBreak(BreakStatement bs)
     {
         /* https://dlang.org/spec/statement.html#break-statement
          */
@@ -3207,7 +2922,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = bs;
     }
 
-    override void visit(ContinueStatement cs)
+    void visitContinue(ContinueStatement cs)
     {
         /* https://dlang.org/spec/statement.html#continue-statement
          */
@@ -3294,7 +3009,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = cs;
     }
 
-    override void visit(SynchronizedStatement ss)
+    void visitSynchronized(SynchronizedStatement ss)
     {
         /* https://dlang.org/spec/statement.html#synchronized-statement
          */
@@ -3416,7 +3131,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
     }
 
-    override void visit(WithStatement ws)
+    void visitWith(WithStatement ws)
     {
         /* https://dlang.org/spec/statement.html#with-statement
          */
@@ -3451,14 +3166,16 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
         else
         {
-            Type t = ws.exp.type.toBasetype();
+            Type texp = ws.exp.type;
+            Type t = texp.toBasetype();
 
             Expression olde = ws.exp;
             if (t.ty == Tpointer)
             {
                 ws.exp = new PtrExp(ws.loc, ws.exp);
                 ws.exp = ws.exp.expressionSemantic(sc);
-                t = ws.exp.type.toBasetype();
+                texp = ws.exp.type;
+                t = texp.toBasetype();
             }
 
             assert(t);
@@ -3506,9 +3223,16 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
                 sym.parent = sc.scopesym;
                 sym.endlinnum = ws.endloc.linnum;
             }
+            else if (auto tenum = texp.isTypeEnum())
+            {
+                ws.exp = new TypeExp(ws.exp.loc, tenum);
+                sym = new WithScopeSymbol(ws);
+                sym.parent = sc.scopesym;
+                sym.endlinnum = ws.endloc.linnum;
+            }
             else
             {
-                ws.error("`with` expressions must be aggregate types or pointers to them, not `%s`", olde.type.toChars());
+                ws.error("`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde.type.toChars());
                 return setError();
             }
         }
@@ -3531,7 +3255,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
     }
 
     // https://dlang.org/spec/statement.html#TryStatement
-    override void visit(TryCatchStatement tcs)
+    void visitTryCatch(TryCatchStatement tcs)
     {
         //printf("TryCatchStatement.semantic()\n");
 
@@ -3635,7 +3359,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = tcs;
     }
 
-    override void visit(TryFinallyStatement tfs)
+    void visitTryFinally(TryFinallyStatement tfs)
     {
         //printf("TryFinallyStatement::semantic()\n");
         tfs.tryBody = sc.tryBody;   // chain on in-flight tryBody
@@ -3675,7 +3399,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = tfs;
     }
 
-    override void visit(ScopeGuardStatement oss)
+    void visitScopeGuard(ScopeGuardStatement oss)
     {
         /* https://dlang.org/spec/statement.html#scope-guard-statement
          */
@@ -3725,7 +3449,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = oss;
     }
 
-    override void visit(ThrowStatement ts)
+    void visitThrow(ThrowStatement ts)
     {
         /* https://dlang.org/spec/statement.html#throw-statement
          */
@@ -3738,57 +3462,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
 
     }
 
-    /**
-     * Run semantic on `throw <exp>`.
-     *
-     * Params:
-     *   loc = location of the `throw`
-     *   exp = value to be thrown
-     *   sc  = enclosing scope
-     *
-     * Returns: true if the `throw` is valid, or false if an error was found
-     */
-    extern(D) static bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc)
-    {
-        if (!global.params.useExceptions)
-        {
-            loc.error("cannot use `throw` statements with -betterC");
-            return false;
-        }
-
-        if (!ClassDeclaration.throwable)
-        {
-            loc.error("cannot use `throw` statements because `object.Throwable` was not declared");
-            return false;
-        }
-
-        if (FuncDeclaration fd = sc.parent.isFuncDeclaration())
-            fd.hasReturnExp |= 2;
-
-        if (exp.op == EXP.new_)
-        {
-            NewExp ne = cast(NewExp) exp;
-            ne.thrownew = true;
-        }
-
-        exp = exp.expressionSemantic(sc);
-        exp = resolveProperties(sc, exp);
-        exp = checkGC(sc, exp);
-        if (exp.op == EXP.error)
-            return false;
-
-        checkThrowEscape(sc, exp, false);
-
-        ClassDeclaration cd = exp.type.toBasetype().isClassHandle();
-        if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
-        {
-            loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars());
-            return false;
-        }
-        return true;
-    }
-
-    override void visit(DebugStatement ds)
+    void visitDebug(DebugStatement ds)
     {
         if (ds.statement)
         {
@@ -3800,7 +3474,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ds.statement;
     }
 
-    override void visit(GotoStatement gs)
+    void visitGoto(GotoStatement gs)
     {
         /* https://dlang.org/spec/statement.html#goto-statement
          */
@@ -3844,7 +3518,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = gs;
     }
 
-    override void visit(LabelStatement ls)
+    void visitLabel(LabelStatement ls)
     {
         //printf("LabelStatement::semantic()\n");
         FuncDeclaration fd = sc.parent.isFuncDeclaration();
@@ -3878,7 +3552,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = ls;
     }
 
-    override void visit(AsmStatement s)
+    void visitAsm(AsmStatement s)
     {
         /* https://dlang.org/spec/statement.html#asm
          */
@@ -3887,7 +3561,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = asmSemantic(s, sc);
     }
 
-    override void visit(CompoundAsmStatement cas)
+    void visitCompoundAsm(CompoundAsmStatement cas)
     {
         //printf("CompoundAsmStatement()::semantic()\n");
         // Apply postfix attributes of the asm block to each statement.
@@ -3915,9 +3589,9 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
 
         assert(sc.func);
-        if (!(cas.stc & STC.pure_) && sc.func.setImpure())
+        if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not"))
             cas.error("`asm` statement is assumed to be impure - mark it with `pure` if it is not");
-        if (!(cas.stc & STC.nogc) && sc.func.setGC())
+        if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not"))
             cas.error("`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
         if (!(cas.stc & (STC.trusted | STC.safe)))
         {
@@ -3928,7 +3602,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         result = cas;
     }
 
-    override void visit(ImportStatement imps)
+    void visitImport(ImportStatement imps)
     {
         /* https://dlang.org/spec/module.html#ImportDeclaration
          */
@@ -3967,8 +3641,375 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
         }
         result = imps;
     }
+
+    mixin VisitStatement!void visit;
+    visit.VisitStatement(s);
+    return result;
 }
 
+/**
+ * Run semantic on `throw <exp>`.
+ *
+ * Params:
+ *   loc = location of the `throw`
+ *   exp = value to be thrown
+ *   sc  = enclosing scope
+ *
+ * Returns: true if the `throw` is valid, or false if an error was found
+ */
+public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc)
+{
+    if (!global.params.useExceptions)
+    {
+        loc.error("cannot use `throw` statements with -betterC");
+        return false;
+    }
+
+    if (!ClassDeclaration.throwable)
+    {
+        loc.error("cannot use `throw` statements because `object.Throwable` was not declared");
+        return false;
+    }
+
+    if (FuncDeclaration fd = sc.parent.isFuncDeclaration())
+        fd.hasReturnExp |= 2;
+
+    if (exp.op == EXP.new_)
+    {
+        NewExp ne = cast(NewExp) exp;
+        ne.thrownew = true;
+    }
+
+    exp = exp.expressionSemantic(sc);
+    exp = resolveProperties(sc, exp);
+    exp = checkGC(sc, exp);
+    if (exp.op == EXP.error)
+        return false;
+    if (!exp.type.isNaked())
+    {
+        // @@@DEPRECATED_2.112@@@
+        // Deprecated in 2.102, change into an error & return false in 2.112
+        exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars());
+        //return false;
+    }
+    checkThrowEscape(sc, exp, false);
+
+    ClassDeclaration cd = exp.type.toBasetype().isClassHandle();
+    if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
+    {
+        loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars());
+        return false;
+    }
+    return true;
+}
+
+private extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde,
+            Type tab, Scope* sc2, Dsymbol sapply)
+{
+    version (none)
+    {
+        if (global.params.useDIP1000 == FeatureState.enabled)
+        {
+            message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
+        }
+        (cast(FuncExp)flde).fd.tookAddressOf = 1;
+    }
+    else
+    {
+        if (global.params.useDIP1000 == FeatureState.enabled)
+            ++(cast(FuncExp)flde).fd.tookAddressOf;  // allocate a closure unless the opApply() uses 'scope'
+    }
+    assert(tab.ty == Tstruct || tab.ty == Tclass);
+    assert(sapply);
+    /* Call:
+     *  aggr.apply(flde)
+     */
+    Expression ec;
+    ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident);
+    ec = new CallExp(fs.loc, ec, flde);
+    ec = ec.expressionSemantic(sc2);
+    if (ec.op == EXP.error)
+        return null;
+    if (ec.type != Type.tint32)
+    {
+        fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
+        return null;
+    }
+    return ec;
+}
+
+private extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde,
+                                                  Type tab, Scope* sc2)
+{
+    Expression ec;
+    /* Call:
+     *      aggr(flde)
+     */
+    if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() &&
+        !(cast(DelegateExp)fs.aggr).func.needThis())
+    {
+        // https://issues.dlang.org/show_bug.cgi?id=3560
+        fs.aggr = (cast(DelegateExp)fs.aggr).e1;
+    }
+    ec = new CallExp(fs.loc, fs.aggr, flde);
+    ec = ec.expressionSemantic(sc2);
+    if (ec.op == EXP.error)
+        return null;
+    if (ec.type != Type.tint32)
+    {
+        fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
+        return null;
+    }
+    return ec;
+}
+
+private extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
+                                               Type tab, Scope* sc2, Type tn, Type tnv)
+{
+    Expression ec;
+    const dim = fs.parameters.length;
+    const loc = fs.loc;
+    /* Call:
+     *      _aApply(aggr, flde)
+     */
+    static immutable fntab =
+    [
+     "cc", "cw", "cd",
+     "wc", "cc", "wd",
+     "dc", "dw", "dd"
+    ];
+
+    const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
+    char[BUFFER_LEN] fdname;
+    int flag;
+
+    switch (tn.ty)
+    {
+        case Tchar:     flag = 0;   break;
+        case Twchar:    flag = 3;   break;
+        case Tdchar:    flag = 6;   break;
+        default:
+            assert(0);
+    }
+    switch (tnv.ty)
+    {
+        case Tchar:     flag += 0;  break;
+        case Twchar:    flag += 1;  break;
+        case Tdchar:    flag += 2;  break;
+        default:
+            assert(0);
+    }
+    const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
+    int j = snprintf(fdname.ptr, BUFFER_LEN,  "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim);
+    assert(j < BUFFER_LEN);
+
+    FuncDeclaration fdapply;
+    TypeDelegate dgty;
+    auto params = new Parameters();
+    params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null));
+    auto dgparams = new Parameters();
+    dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+    if (dim == 2)
+        dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+    dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
+    params.push(new Parameter(0, dgty, null, null, null));
+    fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
+
+    if (tab.isTypeSArray())
+        fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
+    // paint delegate argument to the type runtime expects
+    Expression fexp = flde;
+    if (!dgty.equals(flde.type))
+    {
+        fexp = new CastExp(loc, flde, flde.type);
+        fexp.type = dgty;
+    }
+    ec = new VarExp(Loc.initial, fdapply, false);
+    ec = new CallExp(loc, ec, fs.aggr, fexp);
+    ec.type = Type.tint32; // don't run semantic() on ec
+    return ec;
+}
+
+private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab)
+{
+    auto taa = tab.isTypeAArray();
+    Expression ec;
+    const dim = fs.parameters.length;
+    // Check types
+    Parameter p = (*fs.parameters)[0];
+    bool isRef = (p.storageClass & STC.ref_) != 0;
+    Type ta = p.type;
+    if (dim == 2)
+    {
+        Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
+        if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
+        {
+            fs.error("`foreach`: index must be type `%s`, not `%s`",
+                     ti.toChars(), ta.toChars());
+            return null;
+        }
+        p = (*fs.parameters)[1];
+        isRef = (p.storageClass & STC.ref_) != 0;
+        ta = p.type;
+    }
+    Type taav = taa.nextOf();
+    if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
+    {
+        fs.error("`foreach`: value must be type `%s`, not `%s`",
+                 taav.toChars(), ta.toChars());
+        return null;
+    }
+
+    /* Call:
+     *  extern(C) int _aaApply(void*, in size_t, int delegate(void*))
+     *      _aaApply(aggr, keysize, flde)
+     *
+     *  extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
+     *      _aaApply2(aggr, keysize, flde)
+     */
+    __gshared FuncDeclaration* fdapply = [null, null];
+    __gshared TypeDelegate* fldeTy = [null, null];
+    ubyte i = (dim == 2 ? 1 : 0);
+    if (!fdapply[i])
+    {
+        auto params = new Parameters();
+        params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null));
+        params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null));
+        auto dgparams = new Parameters();
+        dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+        if (dim == 2)
+            dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+        fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
+        params.push(new Parameter(0, fldeTy[i], null, null, null));
+        fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
+    }
+
+    auto exps = new Expressions();
+    exps.push(fs.aggr);
+    auto keysize = taa.index.size();
+    if (keysize == SIZE_INVALID)
+        return null;
+    assert(keysize < keysize.max - target.ptrsize);
+    keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
+    // paint delegate argument to the type runtime expects
+    Expression fexp = flde;
+    if (!fldeTy[i].equals(flde.type))
+    {
+        fexp = new CastExp(fs.loc, flde, flde.type);
+        fexp.type = fldeTy[i];
+    }
+    exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
+    exps.push(fexp);
+    ec = new VarExp(Loc.initial, fdapply[i], false);
+    ec = new CallExp(fs.loc, ec, exps);
+    ec.type = Type.tint32; // don't run semantic() on ec
+    return ec;
+}
+
+private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc)
+{
+    if (!cases.length)
+    {
+        // Easy case, a clean exit from the loop
+        e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
+        return new ExpStatement(loc, e);
+    }
+    // Construct a switch statement around the return value
+    // of the apply function.
+    Statement s;
+    auto a = new Statements();
+
+    // default: break; takes care of cases 0 and 1
+    s = new BreakStatement(Loc.initial, null);
+    s = new DefaultStatement(Loc.initial, s);
+    a.push(s);
+
+    // cases 2...
+    foreach (i, c; *cases)
+    {
+        s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
+        a.push(s);
+    }
+
+    s = new CompoundStatement(loc, a);
+    return new SwitchStatement(loc, e, s, false);
+}
+
+/*************************************
+ * Turn foreach body into the function literal:
+ *  int delegate(ref T param) { body }
+ * Params:
+ *  sc = context
+ *  fs = ForeachStatement
+ *  tfld = type of function literal to be created (type of opApply() function if any), can be null
+ * Returns:
+ *  Function literal created, as an expression
+ *  null if error.
+ */
+private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
+{
+    auto params = new Parameters();
+    foreach (i, p; *fs.parameters)
+    {
+        StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_);
+        Identifier id;
+
+        p.type = p.type.typeSemantic(fs.loc, sc);
+        p.type = p.type.addStorageClass(p.storageClass);
+        if (tfld)
+        {
+            Parameter prm = tfld.parameterList[i];
+            //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
+            stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_);
+            if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_))
+            {
+                if (!(prm.storageClass & STC.ref_))
+                {
+                    fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars());
+                    return null;
+                }
+                goto LcopyArg;
+            }
+            id = p.ident; // argument copy is not need.
+        }
+        else if (p.storageClass & STC.ref_)
+        {
+            // default delegate parameters are marked as ref, then
+            // argument copy is not need.
+            id = p.ident;
+        }
+        else
+        {
+            // Make a copy of the ref argument so it isn't
+            // a reference.
+        LcopyArg:
+            id = Identifier.generateId("__applyArg", cast(int)i);
+
+            Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
+            auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
+            v.storage_class |= STC.temp | (stc & STC.scope_);
+            Statement s = new ExpStatement(fs.loc, v);
+            fs._body = new CompoundStatement(fs.loc, s, fs._body);
+        }
+        params.push(new Parameter(stc, p.type, id, null, null));
+    }
+    // https://issues.dlang.org/show_bug.cgi?id=13840
+    // Throwable nested function inside nothrow function is acceptable.
+    StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
+    auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
+    fs.cases = new Statements();
+    fs.gotos = new ScopeStatements();
+    auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
+    fld.fbody = fs._body;
+    Expression flde = new FuncExp(fs.loc, fld);
+    flde = flde.expressionSemantic(sc);
+    fld.tookAddressOf = 0;
+    if (flde.op == EXP.error)
+        return null;
+    return cast(FuncExp)flde;
+}
+
+
 void catchSemantic(Catch c, Scope* sc)
 {
     //printf("Catch::semantic(%s)\n", ident.toChars());
@@ -4735,8 +4776,8 @@ private Statements* flatten(Statement statement, Scope* sc)
             (*a)[0] = ls;
             return a;
 
-        case STMT.Compile:
-            auto cs = statement.isCompileStatement();
+        case STMT.Mixin:
+            auto cs = statement.isMixinStatement();
 
 
             OutBuffer buf;
@@ -4747,13 +4788,16 @@ private Statements* flatten(Statement statement, Scope* sc)
             const len = buf.length;
             buf.writeByte(0);
             const str = buf.extractSlice()[0 .. len];
-            scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink);
+            const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+            auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
+            scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
+            p.transitionIn = global.params.vin;
             p.nextToken();
 
             auto a = new Statements();
             while (p.token.value != TOK.endOfFile)
             {
-                Statement s = p.parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
+                Statement s = p.parseStatement(ParseStatementFlags.curlyScope);
                 if (!s || global.errors != errors)
                     return errorStatements();
                 a.push(s);
index aec3a77dee8cdf0a6c6952742aa733d15a872277..352c89ef164d4b775686f234fa3c3a99e0fdf206 100644 (file)
@@ -274,6 +274,7 @@ enum TOK : ubyte
     __cdecl,
     __declspec,
     __stdcall,
+    __thread,
     __pragma,
     __int128,
     __attribute__,
@@ -289,8 +290,6 @@ enum EXP : ubyte
     cast_,
     null_,
     assert_,
-    true_,
-    false_,
     array,
     call,
     address,
@@ -307,13 +306,10 @@ enum EXP : ubyte
     dotType,
     slice,
     arrayLength,
-    version_,
     dollar,
     template_,
     dotTemplateDeclaration,
     declaration,
-    typeof_,
-    pragma_,
     dSymbol,
     typeid_,
     uadd,
@@ -394,13 +390,11 @@ enum EXP : ubyte
     int64,
     float64,
     complex80,
-    char_,
     import_,
     delegate_,
     function_,
     mixin_,
     in_,
-    default_,
     break_,
     continue_,
     goto_,
@@ -414,7 +408,6 @@ enum EXP : ubyte
     moduleString,   // __MODULE__
     functionString, // __FUNCTION__
     prettyFunction, // __PRETTY_FUNCTION__
-    shared_,
     pow,
     powAssign,
     vector,
@@ -424,10 +417,11 @@ enum EXP : ubyte
     showCtfeContext,
     objcClassReference,
     vectorArray,
-    arrow,      // ->
     compoundLiteral, // ( type-name ) { initializer-list }
     _Generic,
     interval,
+
+    loweredAssignExp,
 }
 
 enum FirstCKeyword = TOK.inline;
@@ -586,6 +580,7 @@ private immutable TOK[] keywords =
     TOK.__cdecl,
     TOK.__declspec,
     TOK.__stdcall,
+    TOK.__thread,
     TOK.__pragma,
     TOK.__int128,
     TOK.__attribute__,
@@ -617,7 +612,7 @@ static immutable TOK[TOK.max + 1] Ckeywords =
                        union_, unsigned, void_, volatile, while_, asm_, typeof_,
                        _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn,
                        _Static_assert, _Thread_local,
-                       _import, __cdecl, __declspec, __stdcall, __pragma, __int128, __attribute__,
+                       _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__,
                        _assert ];
 
         foreach (kw; Ckwds)
@@ -889,6 +884,7 @@ extern (C++) struct Token
         TOK.__cdecl        : "__cdecl",
         TOK.__declspec     : "__declspec",
         TOK.__stdcall      : "__stdcall",
+        TOK.__thread       : "__thread",
         TOK.__pragma       : "__pragma",
         TOK.__int128       : "__int128",
         TOK.__attribute__  : "__attribute__",
index 87361f327a4f87cac2a7bedd1cc26c4d9f8f4e09..6c1b9792dbb2b8e24a3944869695e41f9c2daa6b 100644 (file)
@@ -283,6 +283,7 @@ enum class TOK : unsigned char
     cdecl_,
     declspec,
     stdcall,
+    thread,
     pragma,
     int128_,
     attribute__,
@@ -299,8 +300,6 @@ enum class EXP : unsigned char
     cast_,
     null_,
     assert_,
-    true_,
-    false_,
     array,
     call,
     address,
@@ -317,13 +316,10 @@ enum class EXP : unsigned char
     dotType,
     slice,
     arrayLength,
-    version_,
     dollar,
     template_,
     dotTemplateDeclaration,
     declaration,
-    typeof_,
-    pragma_,
     dSymbol,
     typeid_,
     uadd,
@@ -404,13 +400,11 @@ enum class EXP : unsigned char
     int64,
     float64,
     complex80,
-    char_,
     import_,
     delegate_,
     function_,
     mixin_,
     in_,
-    default_,
     break_,
     continue_,
     goto_,
@@ -424,7 +418,6 @@ enum class EXP : unsigned char
     moduleString,   // __MODULE__
     functionString, // __FUNCTION__
     prettyFunction, // __PRETTY_FUNCTION__
-    shared_,
     pow,
     powAssign,
     vector,
@@ -434,7 +427,6 @@ enum class EXP : unsigned char
     showCtfeContext,
     objcClassReference,
     vectorArray,
-    arrow,      // ->
     compoundLiteral, // ( type-name ) { initializer-list }
     _Generic_,
     interval,
index de0129b5b332adba5f3882bfb8bb41fccb84e3b5..0f363536d8e3f6c0aaff187d73514d6cf693b61c 100644 (file)
@@ -118,55 +118,40 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data)
     data.setDim(cast(size_t)cntdata);
     data.zero();
 
-    extern (C++) final class PointerBitmapVisitor : Visitor
-    {
-        alias visit = Visitor.visit;
-    public:
-        extern (D) this(Array!(ulong)* _data, ulong _sz_size_t) scope
-        {
-            this.data = _data;
-            this.sz_size_t = _sz_size_t;
-        }
+    ulong offset;
+    bool error;
 
+    void visit(Type t)
+    {
         void setpointer(ulong off)
         {
             ulong ptroff = off / sz_size_t;
             (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t));
         }
 
-        override void visit(Type t)
+        void visitType(Type t)
         {
             Type tb = t.toBasetype();
             if (tb != t)
-                tb.accept(this);
+                visit(tb);
         }
 
-        override void visit(TypeError t)
+        void visitError(TypeError t)
         {
-            visit(cast(Type)t);
+            visitType(t);
         }
 
-        override void visit(TypeNext t)
-        {
-            assert(0);
-        }
-
-        override void visit(TypeBasic t)
+        void visitBasic(TypeBasic t)
         {
             if (t.ty == Tvoid)
                 setpointer(offset);
         }
 
-        override void visit(TypeVector t)
-        {
-        }
-
-        override void visit(TypeArray t)
+        void visitVector(TypeVector t)
         {
-            assert(0);
         }
 
-        override void visit(TypeSArray t)
+        void visitSArray(TypeSArray t)
         {
             ulong arrayoff = offset;
             ulong nextsize = t.next.size();
@@ -176,95 +161,67 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data)
             for (ulong i = 0; i < dim; i++)
             {
                 offset = arrayoff + i * nextsize;
-                t.next.accept(this);
+                visit(t.next);
             }
             offset = arrayoff;
         }
 
-        override void visit(TypeDArray t)
+        void visitDArray(TypeDArray t)
         {
             setpointer(offset + sz_size_t);
         }
 
         // dynamic array is {length,ptr}
-        override void visit(TypeAArray t)
+        void visitAArray(TypeAArray t)
         {
             setpointer(offset);
         }
 
-        override void visit(TypePointer t)
+        void visitPointer(TypePointer t)
         {
             if (t.nextOf().ty != Tfunction) // don't mark function pointers
                 setpointer(offset);
         }
 
-        override void visit(TypeReference t)
+        void visitReference(TypeReference t)
         {
             setpointer(offset);
         }
 
-        override void visit(TypeClass t)
+        void visitClass(TypeClass t)
         {
             setpointer(offset);
         }
 
-        override void visit(TypeFunction t)
+        void visitFunction(TypeFunction t)
         {
         }
 
-        override void visit(TypeDelegate t)
+        void visitDelegate(TypeDelegate t)
         {
             setpointer(offset);
         }
 
-        // delegate is {context, function}
-        override void visit(TypeQualified t)
+        void visitEnum(TypeEnum t)
         {
-            assert(0);
+            visitType(t);
         }
 
-        // assume resolved
-        override void visit(TypeIdentifier t)
+        void visitTuple(TypeTuple t)
         {
-            assert(0);
+            visitType(t);
         }
 
-        override void visit(TypeInstance t)
+        void visitNull(TypeNull t)
         {
-            assert(0);
-        }
-
-        override void visit(TypeTypeof t)
-        {
-            assert(0);
-        }
-
-        override void visit(TypeReturn t)
-        {
-            assert(0);
-        }
-
-        override void visit(TypeEnum t)
-        {
-            visit(cast(Type)t);
-        }
-
-        override void visit(TypeTuple t)
-        {
-            visit(cast(Type)t);
+            // always a null pointer
         }
 
-        override void visit(TypeSlice t)
+        void visitNoreturn(TypeNoreturn t)
         {
-            assert(0);
         }
 
-        override void visit(TypeNull t)
-        {
-            // always a null pointer
-        }
-
-        override void visit(TypeStruct t)
+        void visitStruct(TypeStruct t)
         {
             ulong structoff = offset;
             foreach (v; t.sym.fields)
@@ -273,38 +230,43 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data)
                 if (v.type.ty == Tclass)
                     setpointer(offset);
                 else
-                    v.type.accept(this);
+                    visit(v.type);
             }
             offset = structoff;
         }
 
+        void visitDefaultCase(Type t)
+        {
+            //printf("ty = %d\n", t.ty);
+            assert(0);
+        }
+
+        mixin VisitType!void visit;
+        visit.VisitType(t);
+    }
+
+    if (auto tc = t.isTypeClass())
+    {
         // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
-        void visitClass(TypeClass t)
+        void visitTopLevelClass(TypeClass t)
         {
             ulong classoff = offset;
             // skip vtable-ptr and monitor
             if (t.sym.baseClass)
-                visitClass(cast(TypeClass)t.sym.baseClass.type);
+                visitTopLevelClass(t.sym.baseClass.type.isTypeClass());
             foreach (v; t.sym.fields)
             {
                 offset = classoff + v.offset;
-                v.type.accept(this);
+                visit(v.type);
             }
             offset = classoff;
         }
 
-        Array!(ulong)* data;
-        ulong offset;
-        ulong sz_size_t;
-        bool error;
+        visitTopLevelClass(tc);
     }
-
-    scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t);
-    if (t.ty == Tclass)
-        pbv.visitClass(cast(TypeClass)t);
     else
-        t.accept(pbv);
-    return pbv.error ? ulong.max : sz;
+        visit(t);
+    return error ? ulong.max : sz;
 }
 
 /**
@@ -1314,6 +1276,19 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
             return ErrorExp.get();
         }
 
+        // https://issues.dlang.org/show_bug.cgi?id=19706
+        // When getting the attributes of the instance of a
+        // templated member function semantic tiargs does
+        // not perform semantic3 on the instance.
+        // For more information see FuncDeclaration.functionSemantic.
+        // For getFunctionAttributes it is mandatory to do
+        // attribute inference.
+        if (fd && fd.parent && fd.parent.isTemplateInstance)
+        {
+            fd.functionSemantic3();
+            tf = cast(TypeFunction)fd.type;
+        }
+
         auto mods = new Expressions();
 
         void addToMods(string str)
@@ -1354,6 +1329,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
          *  "argptr"    extern(D) void dstyle(...), use `__argptr` and `__arguments`
          *  "stdarg"    extern(C) void cstyle(int, ...), use core.stdc.stdarg
          *  "typesafe"  void typesafe(T[] ...)
+         *  "KR"        old K+R style
          */
         // get symbol linkage as a string
         if (dim != 1)
@@ -1388,6 +1364,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
             case VarArg.variadic: style = (link == LINK.d)
                                              ? "argptr"
                                              : "stdarg";    break;
+            case VarArg.KRvariadic: style = "KR";           break;
             case VarArg.typesafe: style = "typesafe";       break;
         }
         auto se = new StringExp(e.loc, style);
@@ -2246,65 +2223,65 @@ private void traitNotFound(TraitsExp e)
         // All possible traits
         __gshared Identifier*[59] idents =
         [
+            &Id.allMembers,
+            &Id.child,
+            &Id.classInstanceAlignment,
+            &Id.classInstanceSize,
+            &Id.compiles,
+            &Id.derivedMembers,
+            &Id.fullyQualifiedName,
+            &Id.getAliasThis,
+            &Id.getAttributes,
+            &Id.getFunctionAttributes,
+            &Id.getFunctionVariadicStyle,
+            &Id.getLinkage,
+            &Id.getLocation,
+            &Id.getMember,
+            &Id.getOverloads,
+            &Id.getParameterStorageClasses,
+            &Id.getPointerBitmap,
+            &Id.getProtection,
+            &Id.getTargetInfo,
+            &Id.getUnitTests,
+            &Id.getVirtualFunctions,
+            &Id.getVirtualIndex,
+            &Id.getVirtualMethods,
+            &Id.getVisibility,
+            &Id.hasCopyConstructor,
+            &Id.hasMember,
+            &Id.hasPostblit,
+            &Id.identifier,
             &Id.isAbstractClass,
+            &Id.isAbstractFunction,
             &Id.isArithmetic,
             &Id.isAssociativeArray,
-            &Id.isDisabled,
+            &Id.isCopyable,
             &Id.isDeprecated,
-            &Id.isFuture,
+            &Id.isDisabled,
             &Id.isFinalClass,
-            &Id.isPOD,
-            &Id.isNested,
+            &Id.isFinalFunction,
             &Id.isFloating,
+            &Id.isFuture,
             &Id.isIntegral,
-            &Id.isScalar,
-            &Id.isStaticArray,
-            &Id.isUnsigned,
-            &Id.isVirtualFunction,
-            &Id.isVirtualMethod,
-            &Id.isAbstractFunction,
-            &Id.isFinalFunction,
-            &Id.isOverrideFunction,
-            &Id.isStaticFunction,
+            &Id.isLazy,
             &Id.isModule,
+            &Id.isNested,
+            &Id.isOut,
+            &Id.isOverrideFunction,
             &Id.isPackage,
+            &Id.isPOD,
             &Id.isRef,
-            &Id.isOut,
-            &Id.isLazy,
             &Id.isReturnOnStack,
-            &Id.hasMember,
-            &Id.identifier,
-            &Id.fullyQualifiedName,
-            &Id.getProtection,
-            &Id.getVisibility,
-            &Id.parent,
-            &Id.child,
-            &Id.getLinkage,
-            &Id.getMember,
-            &Id.getOverloads,
-            &Id.getVirtualFunctions,
-            &Id.getVirtualMethods,
-            &Id.classInstanceSize,
-            &Id.classInstanceAlignment,
-            &Id.allMembers,
-            &Id.derivedMembers,
             &Id.isSame,
-            &Id.compiles,
-            &Id.getAliasThis,
-            &Id.getAttributes,
-            &Id.getFunctionAttributes,
-            &Id.getFunctionVariadicStyle,
-            &Id.getParameterStorageClasses,
-            &Id.getUnitTests,
-            &Id.getVirtualIndex,
-            &Id.getPointerBitmap,
+            &Id.isScalar,
+            &Id.isStaticArray,
+            &Id.isStaticFunction,
+            &Id.isUnsigned,
+            &Id.isVirtualFunction,
+            &Id.isVirtualMethod,
             &Id.isZeroInit,
-            &Id.getTargetInfo,
-            &Id.getLocation,
-            &Id.hasPostblit,
-            &Id.hasCopyConstructor,
-            &Id.isCopyable,
             &Id.parameters,
+            &Id.parent,
         ];
 
         StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable;
index 5844911bc6aeb63619750e5f0cadbcce6df0c6e4..c58827063d29e63a15299b9b92341caad3b42745 100644 (file)
@@ -44,9 +44,9 @@ package mixin template ParseVisitMethods(AST)
         }
     }
 
-    override void visit(AST.CompileStatement s)
+    override void visit(AST.MixinStatement s)
     {
-        //printf("Visiting CompileStatement\n");
+        //printf("Visiting MixinStatement\n");
         visitArgs(s.exps.peekSlice());
     }
 
@@ -579,7 +579,7 @@ package mixin template ParseVisitMethods(AST)
             de.accept(this);
     }
 
-    override void visit(AST.CompileDeclaration d)
+    override void visit(AST.MixinDeclaration d)
     {
         //printf("Visiting compileDeclaration\n");
         visitArgs(d.exps.peekSlice());
index c668199e5f73a53a05625126e077fa8301fa4a85..f0decf2a48daf1a57996e47c8ace461aff1ff781 100644 (file)
@@ -290,6 +290,8 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb
         if (!sm)
             return helper3();
 
+        if (sm.isAliasDeclaration)
+            sm.checkDeprecated(loc, sc);
         s = sm.toAlias();
     }
 
@@ -456,6 +458,17 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
         return t.merge();
     }
 
+    Type visitComplex(TypeBasic t)
+    {
+        if (!(sc.flags & SCOPE.Cfile))
+            return visitType(t);
+
+        auto tc = getComplexLibraryType(loc, sc, t.ty);
+        if (tc.ty == Terror)
+            return tc;
+        return tc.addMod(t.mod).merge();
+    }
+
     Type visitVector(TypeVector mtype)
     {
         const errors = global.errors;
@@ -1185,19 +1198,31 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
 
                 // -preview=in: Always add `ref` when used with `extern(C++)` functions
                 // Done here to allow passing opaque types with `in`
-                if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_)
+                if ((fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_)
                 {
                     switch (tf.linkage)
                     {
                     case LINK.cpp:
-                        fparam.storageClass |= STC.ref_;
+                        if (global.params.previewIn)
+                            fparam.storageClass |= STC.ref_;
                         break;
                     case LINK.default_, LINK.d:
                         break;
                     default:
-                        .error(loc, "cannot use `in` parameters with `extern(%s)` functions",
-                               linkageToChars(tf.linkage));
-                        .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars());
+                        if (global.params.previewIn)
+                        {
+                            .error(loc, "cannot use `in` parameters with `extern(%s)` functions",
+                                   linkageToChars(tf.linkage));
+                            .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars());
+                        }
+                        else
+                        {
+                            // Note that this deprecation will not trigger on `in ref` / `ref in`
+                            // parameters, however the parser will trigger a deprecation on them.
+                            .deprecation(loc, "using `in` parameters with `extern(%s)` functions is deprecated",
+                                         linkageToChars(tf.linkage));
+                            .deprecationSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars());
+                        }
                         break;
                     }
                 }
@@ -1292,28 +1317,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                     //    error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)");
                 }
 
-                /* Scope attribute is not necessary if the parameter type does not have pointers
-                 */
-                const sr = buildScopeRef(fparam.storageClass);
-                switch (sr)
-                {
-                    case ScopeRef.Scope:
-                    case ScopeRef.RefScope:
-                    case ScopeRef.ReturnRef_Scope:
-                        if (!fparam.type.hasPointers())
-                            fparam.storageClass &= ~STC.scope_;
-                        break;
-
-                    case ScopeRef.ReturnScope:
-                    case ScopeRef.Ref_ReturnScope:
-                        if (!fparam.type.hasPointers())
-                            fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope);
-                        break;
-
-                    default:
-                        break;
-                }
-
                 // Remove redundant storage classes for type, they are already applied
                 fparam.storageClass &= ~(STC.TYPECTOR);
 
@@ -1786,12 +1789,14 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
 
                 case TOK.struct_:
                     auto sd = new StructDeclaration(mtype.loc, mtype.id, false);
+                    sd.alignment = mtype.packalign;
                     declare(sd);
                     mtype.resolved = visitStruct(new TypeStruct(sd));
                     break;
 
                 case TOK.union_:
                     auto ud = new UnionDeclaration(mtype.loc, mtype.id);
+                    ud.alignment = mtype.packalign;
                     declare(ud);
                     mtype.resolved = visitStruct(new TypeStruct(ud));
                     break;
@@ -1928,6 +1933,9 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
     switch (type.ty)
     {
         default:         return visitType(type);
+        case Tcomplex32:
+        case Tcomplex64:
+        case Tcomplex80: return visitComplex(type.isTypeBasic());
         case Tvector:    return visitVector(type.isTypeVector());
         case Tsarray:    return visitSArray(type.isTypeSArray());
         case Tarray:     return visitDArray(type.isTypeDArray());
@@ -2697,7 +2705,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
             {
                 void semanticOnMixin(Dsymbol member)
                 {
-                    if (auto compileDecl = member.isCompileDeclaration())
+                    if (auto compileDecl = member.isMixinDeclaration())
                         compileDecl.dsymbolSemantic(sc);
                     else if (auto mixinTempl = member.isTemplateMixin())
                         mixinTempl.dsymbolSemantic(sc);
@@ -2796,8 +2804,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
         }
         mt.exp = exp2;
 
-        if (mt.exp.op == EXP.type ||
-            mt.exp.op == EXP.scope_)
+        if ((mt.exp.op == EXP.type || mt.exp.op == EXP.scope_) &&
+            // https://issues.dlang.org/show_bug.cgi?id=23863
+            // compile time sequences are valid types
+            !mt.exp.type.isTypeTuple())
         {
             if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof
                 mt.exp.checkType())
@@ -3126,7 +3136,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
  * Returns:
  *  resulting expression with e.ident resolved
  */
-Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
+Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag)
 {
     Expression visitType(Type mt)
     {
@@ -3624,7 +3634,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
                  *  template opDispatch(name) if (isValid!name) { ... }
                  */
                 uint errors = gagError ? global.startGagging() : 0;
-                e = dti.dotTemplateSemanticProp(sc, 0);
+                e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none);
                 if (gagError && global.endGagging(errors))
                     e = null;
                 return returnExp(e);
@@ -3730,6 +3740,9 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
         {
             return noMember(mt, sc, e, ident, flag);
         }
+        // check before alias resolution; the alias itself might be deprecated!
+        if (s.isAliasDeclaration)
+            e.checkDeprecated(sc, s);
         s = s.toAlias();
 
         if (auto em = s.isEnumMember())
@@ -3917,7 +3930,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
                 return mt.getProperty(sc, e.loc, ident, flag & 1);
             }
 
-            Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1);
+            Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag);
             if (!(flag & 1) && !res)
             {
                 if (auto ns = mt.sym.search_correct(ident))
@@ -4599,6 +4612,74 @@ extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi
     }
 }
 
+
+/**********************************************
+ * Extract complex type from core.stdc.config
+ * Params:
+ *      loc = for error messages
+ *      sc = context
+ *      ty = a complex or imaginary type
+ * Returns:
+ *      Complex!float, Complex!double, Complex!real or null for error
+ */
+
+Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty)
+{
+    // singleton
+    __gshared Type complex_float;
+    __gshared Type complex_double;
+    __gshared Type complex_real;
+
+    Type* pt;
+    Identifier id;
+    switch (ty)
+    {
+        case Timaginary32:
+        case Tcomplex32:   id = Id.c_complex_float;  pt = &complex_float;  break;
+        case Timaginary64:
+        case Tcomplex64:   id = Id.c_complex_double; pt = &complex_double; break;
+        case Timaginary80:
+        case Tcomplex80:   id = Id.c_complex_real;   pt = &complex_real;   break;
+        default:
+             return Type.terror;
+    }
+
+    if (*pt)
+        return *pt;
+    *pt = Type.terror;
+
+    Module mConfig = Module.loadCoreStdcConfig();
+    if (!mConfig)
+    {
+        error(loc, "`core.stdc.config` is required for complex numbers");
+        return *pt;
+    }
+
+    Dsymbol s = mConfig.searchX(Loc.initial, sc, id, IgnorePrivateImports);
+    if (!s)
+    {
+        error(loc, "`%s` not found in core.stdc.config", id.toChars());
+        return *pt;
+    }
+    s = s.toAlias();
+    if (auto t = s.getType())
+    {
+        if (auto ts = t.toBasetype().isTypeStruct())
+        {
+            *pt = ts;
+            return ts;
+        }
+    }
+    if (auto sd = s.isStructDeclaration())
+    {
+        *pt = sd.type;
+        return sd.type;
+    }
+
+    error(loc, "`%s` must be an alias for a complex struct", s.toChars());
+    return *pt;
+}
+
 /******************************* Private *****************************************/
 
 private:
@@ -4909,7 +4990,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id)
  * Return:
  *      null if error, else RootObject AST as parsed
  */
-RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc)
+RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc)
 {
     OutBuffer buf;
     if (expressionsToString(buf, sc, tm.exps))
@@ -4919,7 +5000,10 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc)
     const len = buf.length;
     buf.writeByte(0);
     const str = buf.extractSlice()[0 .. len];
-    scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink);
+    const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+    auto locm = adjustLocForMixin(str, loc, global.params.mixinOut);
+    scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
+    p.transitionIn = global.params.vin;
     p.nextToken();
     //printf("p.loc.linnum = %d\n", p.loc.linnum);
 
index 38a39b4610871018c4154cfa4503c248c39c96e3..4f87d92b2221a6c67c777df7669481ca4a84ab8c 100644 (file)
@@ -47,6 +47,7 @@ extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope
     {
         if (!global.params.useTypeInfo)
         {
+            global.gag = 0;
             if (e)
                 .error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars());
             else
index 8990ce49a2a9dd7aedae0bdc280221709d165645..7b059a061fd9406c22e668ef873c3ef4fde5cd81 100644 (file)
@@ -89,6 +89,7 @@ public:
     void visit(ASTCodegen.ClassReferenceExp e) { visit(cast(ASTCodegen.Expression)e); }
     void visit(ASTCodegen.VoidInitExp e) { visit(cast(ASTCodegen.Expression)e); }
     void visit(ASTCodegen.ThrownExceptionExp e) { visit(cast(ASTCodegen.Expression)e); }
+    void visit(ASTCodegen.LoweredAssignExp e) { visit(cast(ASTCodegen.AssignExp)e); }
 }
 
 /**
@@ -152,7 +153,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor
         // need to avoid infinite recursion.
         if (!(e.stageflags & stageToCBuffer))
         {
-            int old = e.stageflags;
+            const old = e.stageflags;
             e.stageflags |= stageToCBuffer;
             foreach (el; *e.elements)
                 if (el)
@@ -240,6 +241,12 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor
         e.e1.accept(this);
         e.e2.accept(this);
     }
+
+    override void visit(ASTCodegen.LoweredAssignExp e)
+    {
+        e.lowering.accept(this);
+        visit(cast(AssignExp)e);
+    }
 }
 
 extern (C++) class StoppableVisitor : Visitor
index ed9f9ce1f6f736844355c8aa6431b363d586ed88..3d8c3e60220c79d0e458d3d270ed22602cf1d68b 100644 (file)
@@ -17,7 +17,7 @@ class ErrorStatement;
 class PeelStatement;
 class ExpStatement;
 class DtorExpStatement;
-class CompileStatement;
+class MixinStatement;
 class CompoundStatement;
 class CompoundDeclarationStatement;
 class UnrolledLoopStatement;
@@ -110,7 +110,7 @@ class AnonDeclaration;
 class PragmaDeclaration;
 class ConditionalDeclaration;
 class StaticIfDeclaration;
-class CompileDeclaration;
+class MixinDeclaration;
 class StaticForeachDeclaration;
 class UserAttributeDeclaration;
 class ForwardingAttribDeclaration;
@@ -268,6 +268,7 @@ class UshrAssignExp;
 class CatAssignExp;
 class CatElemAssignExp;
 class CatDcharAssignExp;
+class LoweredAssignExp;
 class AddExp;
 class MinExp;
 class CatExp;
@@ -365,7 +366,7 @@ public:
     virtual void visit(SharedStaticDtorDeclaration *s) { visit((StaticDtorDeclaration *)s); }
 
     // AttribDeclarations
-    virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); }
+    virtual void visit(MixinDeclaration *s) { visit((AttribDeclaration *)s); }
     virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); }
     virtual void visit(LinkDeclaration *s) { visit((AttribDeclaration *)s); }
     virtual void visit(AnonDeclaration *s) { visit((AttribDeclaration *)s); }
@@ -396,7 +397,7 @@ public:
     virtual void visit(ReturnStatement *s) { visit((Statement *)s); }
     virtual void visit(LabelStatement *s) { visit((Statement *)s); }
     virtual void visit(StaticAssertStatement *s) { visit((Statement *)s); }
-    virtual void visit(CompileStatement *s) { visit((Statement *)s); }
+    virtual void visit(MixinStatement *s) { visit((Statement *)s); }
     virtual void visit(WhileStatement *s) { visit((Statement *)s); }
     virtual void visit(ForStatement *s) { visit((Statement *)s); }
     virtual void visit(DoStatement *s) { visit((Statement *)s); }
@@ -659,6 +660,7 @@ public:
     virtual void visit(ClassReferenceExp *e) { visit((Expression *)e); }
     virtual void visit(VoidInitExp *e) { visit((Expression *)e); }
     virtual void visit(ThrownExceptionExp *e) { visit((Expression *)e); }
+    virtual void visit(LoweredAssignExp *e) { visit((AssignExp *)e); }
 };
 
 class StoppableVisitor : public Visitor
index aeafe433ca5c016f425129654d0b44e8c4ff3b43..23f2f0b1c8d9e9579ec08d01d9cf9f6b3a0e2ce1 100644 (file)
@@ -693,78 +693,22 @@ public:
 
   void visit (CatExp *e) final override
   {
-    Type *tb1 = e->e1->type->toBasetype ();
-    Type *tb2 = e->e2->type->toBasetype ();
-    Type *etype;
-
-    if (tb1->ty == TY::Tarray || tb1->ty == TY::Tsarray)
-      etype = tb1->nextOf ();
-    else
-      etype = tb2->nextOf ();
-
-    tree result;
-
-    if (e->e1->op == EXP::concatenate)
+    /* This error is only emitted during the code generation pass because
+       concatentation is allowed in CTFE.  */
+    if (global.params.betterC)
       {
-       /* Flatten multiple concatenations to an array.
-          So the expression ((a ~ b) ~ c) becomes [a, b, c]  */
-       int ndims = 2;
-
-       for (Expression *ex = e->e1; ex->op == EXP::concatenate;)
-         {
-           if (ex->op == EXP::concatenate)
-             {
-               ex = ex->isCatExp ()->e1;
-               ndims++;
-             }
-         }
-
-       /* Store all concatenation args to a temporary byte[][ndims] array.  */
-       Type *targselem = Type::tint8->arrayOf ();
-       tree var = build_local_temp (make_array_type (targselem, ndims));
-
-       /* Loop through each concatenation from right to left.  */
-       vec <constructor_elt, va_gc> *elms = NULL;
-       CatExp *ce = e;
-       int dim = ndims - 1;
-
-       for (Expression *oe = ce->e2; oe != NULL;
-            (ce->e1->op != EXP::concatenate
-             ? (oe = ce->e1)
-             : (ce = ce->e1->isCatExp (), oe = ce->e2)))
-         {
-           tree arg = d_array_convert (etype, oe);
-           tree index = size_int (dim);
-           CONSTRUCTOR_APPEND_ELT (elms, index, d_save_expr (arg));
-
-           /* Finished pushing all arrays.  */
-           if (oe == ce->e1)
-             break;
-
-           dim -= 1;
-         }
-
-       /* Check there is no logic bug in constructing byte[][] of arrays.  */
-       gcc_assert (dim == 0);
-       tree init = build_constructor (TREE_TYPE (var), elms);
-       var = compound_expr (modify_expr (var, init), var);
-
-       tree arrs = d_array_value (build_ctype (targselem->arrayOf ()),
-                                  size_int (ndims), build_address (var));
-
-       result = build_libcall (LIBCALL_ARRAYCATNTX, e->type, 2,
-                               build_typeinfo (e, e->type), arrs);
-      }
-    else
-      {
-       /* Handle single concatenation (a ~ b).  */
-       result = build_libcall (LIBCALL_ARRAYCATT, e->type, 3,
-                               build_typeinfo (e, e->type),
-                               d_array_convert (etype, e->e1),
-                               d_array_convert (etype, e->e2));
+       error_at (make_location_t (e->loc),
+                 "array concatenation of expression %qs requires the GC and "
+                 "cannot be used with %<-fno-druntime%>", e->toChars ());
+       this->result_ = error_mark_node;
+       return;
       }
 
-    this->result_ = result;
+    /* All concat expressions should have been rewritten to `_d_arraycatnTX` in
+       the semantic phase.  */
+    gcc_assert (e->lowering);
+
+    this->result_ = build_expr (e->lowering);
   }
 
   /* Build an assignment operator expression.  The right operand is implicitly
@@ -1150,6 +1094,13 @@ public:
     this->result_ = build_assign (modifycode, t1, t2);
   }
 
+  /* Build an assignment expression that has been lowered in the front-end.  */
+
+  void visit (LoweredAssignExp *e) final override
+  {
+    this->result_ = build_expr (e->lowering);
+  }
+
   /* Build a throw expression.  */
 
   void visit (ThrowExp *e) final override
@@ -2828,7 +2779,7 @@ public:
 
     /* Building sinit trees are delayed until after frontend semantic
        processing has complete.  Build the static initializer now.  */
-    if (e->useStaticInit && !this->constp_)
+    if (e->useStaticInit && !this->constp_ && !e->sd->isCsymbol ())
       {
        tree init = aggregate_initializer_decl (e->sd);
 
index 23f8b64324efb4546c07eac4fbfbfe444eaf4de8..fd83cc9748c362f27a4b5ec3868175d781955b5b 100644 (file)
@@ -112,13 +112,6 @@ DEF_D_RUNTIME (ALLOCMEMORY, "_d_allocmemory", RT(VOIDPTR), P1(SIZE_T),
 DEF_D_RUNTIME (ARRAYCOPY, "_d_arraycopy", RT(ARRAY_VOID),
               P3(SIZE_T, ARRAY_VOID, ARRAY_VOID), 0)
 
-/* Used for concatenating two or more arrays together.  Then `n' variant is
-   for when there is more than two arrays to handle.  */
-DEF_D_RUNTIME (ARRAYCATT, "_d_arraycatT", RT(ARRAY_BYTE),
-              P3(CONST_TYPEINFO, ARRAY_BYTE, ARRAY_BYTE), 0)
-DEF_D_RUNTIME (ARRAYCATNTX, "_d_arraycatnTX", RT(ARRAY_VOID),
-              P2(CONST_TYPEINFO, ARRAYARRAY_BYTE), 0)
-
 /* Used for appending a single element to an array.  */
 DEF_D_RUNTIME (ARRAYAPPENDCTX, "_d_arrayappendcTX", RT(ARRAY_BYTE),
               P3(CONST_TYPEINFO, ARRAYPTR_BYTE, SIZE_T), 0)
diff --git a/gcc/testsuite/gdc.dg/nogc1.d b/gcc/testsuite/gdc.dg/nogc1.d
new file mode 100644 (file)
index 0000000..2894ef0
--- /dev/null
@@ -0,0 +1,8 @@
+// { dg-do compile }
+// { dg-options "-fno-druntime" }
+// { dg-shouldfail "expressions depend on GC" }
+
+string testConcat(string a, string b)
+{
+    return a ~ b; // { dg-error "requires the GC and cannot be used with .-fno-druntime." }
+}
index ed5f3440689e14729a9420b15be275c033c824c6..29b8265c82130b24171e6b99d4f8f11945a2b33e 100644 (file)
@@ -11,8 +11,3 @@ bool testAAEqual(int[string] aa1, int[string] aa2)
 {
     return aa1 == aa2; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." }
 }
-
-string testConcat(string a, string b)
-{
-    return a ~ b; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." }
-}
index 870387c7113e61e1218a4b007e8c82358e47f0c2..04363711bd3da885acb5c10f628567f5a35e1cf6 100644 (file)
@@ -48,7 +48,7 @@ namespace nameSpace
         extern void fn2();
 
     }
-    extern double identity(double _param_0);
+    extern double identity(double __param_0_);
 
 }
 ---
@@ -63,5 +63,5 @@ extern(C++, "nameSpace")
         void fn2() {}
     }
 
-    double identity(double) { return _param_0; }
+    double identity(double) { return __param_0; }
 }
index 1feff408adcee0d84bb8b25560a3b61b86834547..38607f6d4b1654a4db988850dbef69923df2ea05 100644 (file)
@@ -159,7 +159,7 @@ extern int32_t(*f)(int32_t );
 
 extern void special(int32_t a = ptr->i, int32_t b = ptr->get(1, 2), int32_t j = (*f)(1));
 
-extern void variadic(int32_t _param_0, ...);
+extern void variadic(int32_t __param_0_, ...);
 ---
 +/
 
index b8e8d05649cda8568029a653362d16b95ac392c0..a8f5b99041373912f2ae992f40717247680ba7cb 100644 (file)
@@ -110,7 +110,7 @@ struct InvalidNames final
     }
 };
 
-extern void useInvalid(InvalidNames<int32_t > _param_0);
+extern void useInvalid(InvalidNames<int32_t > __param_0_);
 
 extern size_t offsetof();
 
index 37b450703a929b38c2ca174418878ace7ce4e56f..ee86a5e712189149abaf61d45d6d018c61b4f09c 100644 (file)
@@ -40,25 +40,25 @@ struct _d_dynamicArray final
 #endif
 
 enum class __c_not_special;
-extern "C" void fn_long(long _param_0);
+extern "C" void fn_long(long __param_0_);
 
-extern "C" void fn_ulong(unsigned long _param_0);
+extern "C" void fn_ulong(unsigned long __param_0_);
 
-extern "C" void fn_longlong(long long _param_0);
+extern "C" void fn_longlong(long long __param_0_);
 
-extern "C" void fn_ulonglong(unsigned long long _param_0);
+extern "C" void fn_ulonglong(unsigned long long __param_0_);
 
-extern "C" void fn_long_double(long double _param_0);
+extern "C" void fn_long_double(long double __param_0_);
 
-extern "C" void fn_wchar_t(wchar_t _param_0);
+extern "C" void fn_wchar_t(wchar_t __param_0_);
 
-extern "C" void fn_complex_float(_Complex float _param_0);
+extern "C" void fn_complex_float(_Complex float __param_0_);
 
-extern "C" void fn_complex_double(_Complex double _param_0);
+extern "C" void fn_complex_double(_Complex double __param_0_);
 
-extern "C" void fn_complex_real(_Complex long double _param_0);
+extern "C" void fn_complex_real(_Complex long double __param_0_);
 
-extern "C" void fn_not_special(__c_not_special _param_0);
+extern "C" void fn_not_special(__c_not_special __param_0_);
 
 ---
 +/
diff --git a/gcc/testsuite/gdc.test/compilable/imports/c23789.i b/gcc/testsuite/gdc.test/compilable/imports/c23789.i
new file mode 100644 (file)
index 0000000..74d7e80
--- /dev/null
@@ -0,0 +1,31 @@
+// https://issues.dlang.org/show_bug.cgi?id=23789
+
+struct __declspec(align(64)) M128A {
+    char c;
+};
+
+typedef struct __declspec(align(32)) _M128B {
+    int x;
+} M128B, *PM128A;
+
+
+void testpl(p)
+struct __declspec(align(2)) S *p;
+{
+}
+
+/////
+
+struct __attribute__((aligned(64))) N128A {
+    char c;
+};
+
+typedef struct __attribute__((aligned(32))) _N128B {
+    int x;
+} N128B, *PN128A;
+
+
+void testpl2(p)
+struct __attribute__((aligned(2))) S *p;
+{
+}
index 2c9a84eddfb804ac7185e52a3b8798458bb91871..14142630454fe9f8bcc560dbe360137d025a9198 100644 (file)
@@ -7208,7 +7208,7 @@ struct S13630(T)
 {
     T[3] arr;
 
-    this(A...)(auto ref in A args)
+    this(A...)(const auto ref A args)
     {
         auto p = arr.ptr;
 
@@ -7238,7 +7238,7 @@ struct Matrix13827(T, uint N)
         T[N] flat;
     }
 
-    this(A...)(auto ref in A args)
+    this(A...)(const auto ref A args)
     {
         uint k;
 
diff --git a/gcc/testsuite/gdc.test/compilable/test18493.d b/gcc/testsuite/gdc.test/compilable/test18493.d
new file mode 100644 (file)
index 0000000..558bf44
--- /dev/null
@@ -0,0 +1,19 @@
+// https://issues.dlang.org/show_bug.cgi?id=18493
+// REQUIRED_ARGS: -betterC
+
+struct S
+{
+    this(this)
+    {
+    }
+
+    ~this()
+    {
+    }
+}
+
+struct C
+{
+    S s1;
+    S s2;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21667.d b/gcc/testsuite/gdc.test/compilable/test21667.d
new file mode 100644 (file)
index 0000000..6d83dc0
--- /dev/null
@@ -0,0 +1,19 @@
+// Issue 21667 - scope parameter causes 'no size because of forward references'
+// https://issues.dlang.org/show_bug.cgi?id=21667
+@safe:
+
+struct Foo
+{
+    void delegate(scope Foo) dg;
+}
+
+struct M
+{
+    F.Type f;
+}
+
+struct F
+{
+    enum Type {a}
+    void foo(scope M m) {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test23789.d b/gcc/testsuite/gdc.test/compilable/test23789.d
new file mode 100644 (file)
index 0000000..ddf7351
--- /dev/null
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=23789
+// EXTRA_FILES: imports/c23789.i
+
+import imports.c23789;
+
+static assert(M128A.alignof == 64);
+static assert(_M128B.alignof == 32);
+static assert(M128B.alignof == 32);
+
+static assert(N128A.alignof == 64);
+static assert(_N128B.alignof == 32);
+static assert(N128B.alignof == 32);
diff --git a/gcc/testsuite/gdc.test/compilable/test23862.d b/gcc/testsuite/gdc.test/compilable/test23862.d
new file mode 100644 (file)
index 0000000..c5b063b
--- /dev/null
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=23862
+
+enum E { A, B }
+
+void test(E e)
+{
+    with (e)
+    switch (e)
+    {
+        case A:
+        case B:
+        default:
+            break;
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test23863.d b/gcc/testsuite/gdc.test/compilable/test23863.d
new file mode 100644 (file)
index 0000000..c3941ce
--- /dev/null
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=23863
+
+alias AliasSeq(T...) = T;
+
+struct S
+{
+}
+alias Empty = S.tupleof;
+Empty x; // accepts valid
+
+static assert(is(typeof(x)));
+static assert(is(typeof(Empty)));
+static assert(is(typeof(AliasSeq!(int))));
+static assert(is(typeof(Empty) == AliasSeq!()));
+static assert(is(typeof(AliasSeq!()) == AliasSeq!()));
index 1f25b26905366a39608cd3fcc558d6915b2e171f..f4defb48095a9b3c1fe9988f99f39ed53a1ad24d 100644 (file)
@@ -1,9 +1,10 @@
 
 module traits_getFunctionAttributes;
 
+alias tuple(T...) = T;
+
 void test_getFunctionAttributes()
 {
-    alias tuple(T...) = T;
 
     struct S
     {
@@ -118,3 +119,14 @@ void test_getFunctionAttributes()
     static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system"));
     static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system"));
 }
+
+void bug19706()
+{
+    struct S
+    {
+        static int fImpl(Ret)() { return Ret.init; }
+
+        // tells us: `fImpl!int` is @system
+        static assert(__traits(getFunctionAttributes, fImpl!int) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/user_defined_attributes.d b/gcc/testsuite/gdc.test/compilable/user_defined_attributes.d
new file mode 100644 (file)
index 0000000..169ca49
--- /dev/null
@@ -0,0 +1,22 @@
+
+enum Test;
+
+@true @null @byte int x;
+@(int) int y;
+@"test" @`test2` @30 @'a' @__LINE__ void f();
+
+@Test void h();
+
+static assert(   __traits(getAttributes, x)[0] == true);
+static assert(   __traits(getAttributes, x)[1] == null);
+static assert(is(__traits(getAttributes, x)[2] == byte));
+
+static assert(is(__traits(getAttributes, y)[0] == int));
+
+static assert(   __traits(getAttributes, f)[0] == "test");
+static assert(   __traits(getAttributes, f)[1] == "test2");
+static assert(   __traits(getAttributes, f)[2] == 30);
+static assert(   __traits(getAttributes, f)[3] == 'a');
+static assert(   __traits(getAttributes, f)[4] == 6);
+
+static assert(is(__traits(getAttributes, h)[0] == enum));
index f02a87bd04b53b9804ba4292606733fd21c53de3..0474315be57e62efa6e70d6af5a89c585693a40e 100644 (file)
@@ -12,7 +12,7 @@ void test3882()
 /******************************************/
 // https://issues.dlang.org/show_bug.cgi?id=12619
 
-extern (C) @system nothrow pure void* memcpy(void* s1, in void* s2, size_t n);
+extern (C) @system nothrow pure void* memcpy(void* s1, const void* s2, size_t n);
 // -> weakly pure
 
 void test12619() pure
@@ -64,7 +64,7 @@ void test12909()
 
 const struct Foo13899
 {
-    int opApply(immutable int delegate(in ref int) pure nothrow dg) pure nothrow
+    int opApply(immutable int delegate(const ref int) pure nothrow dg) pure nothrow
     {
         return 1;
     }
index 8360e1ac4844b38a14f7b1879e4a4ecc303e9d03..523a183d767370816c888a2dbf542a842d4c6cc1 100644 (file)
@@ -4,15 +4,15 @@ TEST_OUTPUT:
 fail_compilation/attributediagnostic.d(24): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1`
 fail_compilation/attributediagnostic.d(26):        which calls `attributediagnostic.layer0`
 fail_compilation/attributediagnostic.d(28):        which calls `attributediagnostic.system`
-fail_compilation/attributediagnostic.d(30):        which was inferred `@system` because of:
+fail_compilation/attributediagnostic.d(30):        which wasn't inferred `@safe` because of:
 fail_compilation/attributediagnostic.d(30):        `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not
 fail_compilation/attributediagnostic.d(25):        `attributediagnostic.layer1` is declared here
 fail_compilation/attributediagnostic.d(46): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system1`
-fail_compilation/attributediagnostic.d(35):        which was inferred `@system` because of:
+fail_compilation/attributediagnostic.d(35):        which wasn't inferred `@safe` because of:
 fail_compilation/attributediagnostic.d(35):        cast from `uint` to `int*` not allowed in safe code
 fail_compilation/attributediagnostic.d(33):        `attributediagnostic.system1` is declared here
 fail_compilation/attributediagnostic.d(47): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system2`
-fail_compilation/attributediagnostic.d(41):        which was inferred `@system` because of:
+fail_compilation/attributediagnostic.d(41):        which wasn't inferred `@safe` because of:
 fail_compilation/attributediagnostic.d(41):        `@safe` function `system2` cannot call `@system` `fsys`
 fail_compilation/attributediagnostic.d(39):        `attributediagnostic.system2` is declared here
 ---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_nogc.d b/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_nogc.d
new file mode 100644 (file)
index 0000000..e3dbee8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/attributediagnostic_nogc.d(21): Error: `@nogc` function `attributediagnostic_nogc.layer2` cannot call non-@nogc function `attributediagnostic_nogc.layer1`
+fail_compilation/attributediagnostic_nogc.d(22):        which calls `attributediagnostic_nogc.layer0`
+fail_compilation/attributediagnostic_nogc.d(23):        which calls `attributediagnostic_nogc.gc`
+fail_compilation/attributediagnostic_nogc.d(27):        which wasn't inferred `@nogc` because of:
+fail_compilation/attributediagnostic_nogc.d(27):        `asm` statement in function `attributediagnostic_nogc.gc` is assumed to use the GC - mark it with `@nogc` if it does not
+fail_compilation/attributediagnostic_nogc.d(43): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc1`
+fail_compilation/attributediagnostic_nogc.d(32):        which wasn't inferred `@nogc` because of:
+fail_compilation/attributediagnostic_nogc.d(32):        cannot use `new` in `@nogc` function `attributediagnostic_nogc.gc1`
+fail_compilation/attributediagnostic_nogc.d(44): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc2`
+fail_compilation/attributediagnostic_nogc.d(38):        which wasn't inferred `@nogc` because of:
+fail_compilation/attributediagnostic_nogc.d(38):        `@nogc` function `attributediagnostic_nogc.gc2` cannot call non-@nogc `fgc`
+fail_compilation/attributediagnostic_nogc.d(45): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gcClosure`
+fail_compilation/attributediagnostic_nogc.d(48):        which wasn't inferred `@nogc` because of:
+fail_compilation/attributediagnostic_nogc.d(48):        function `attributediagnostic_nogc.gcClosure` is `@nogc` yet allocates closure for `gcClosure()` with the GC
+---
+*/
+#line 18
+// Issue 17374 - Improve inferred attribute error message
+// https://issues.dlang.org/show_bug.cgi?id=17374
+
+auto layer2() @nogc { layer1(); }
+auto layer1() { layer0(); }
+auto layer0() { gc(); }
+
+auto gc()
+{
+    asm {}
+}
+
+auto gc1()
+{
+    int* x = new int;
+}
+
+auto fgc = function void() {new int[10];};
+auto gc2()
+{
+    fgc();
+}
+
+void main() @nogc
+{
+    gc1();
+    gc2();
+    gcClosure();
+}
+
+auto gcClosure()
+{
+    int x;
+    int bar() { return x; }
+    return &bar;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_nothrow.d b/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_nothrow.d
new file mode 100644 (file)
index 0000000..7fea322
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer1` is not `nothrow`
+fail_compilation/attributediagnostic_nothrow.d(22):        which calls `attributediagnostic_nothrow.layer0`
+fail_compilation/attributediagnostic_nothrow.d(23):        which calls `attributediagnostic_nothrow.gc`
+fail_compilation/attributediagnostic_nothrow.d(27):        which wasn't inferred `nothrow` because of:
+fail_compilation/attributediagnostic_nothrow.d(27):        `asm` statement is assumed to throw - mark it with `nothrow` if it does not
+fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer2` may throw but is marked as `nothrow`
+fail_compilation/attributediagnostic_nothrow.d(43): Error: function `attributediagnostic_nothrow.gc1` is not `nothrow`
+fail_compilation/attributediagnostic_nothrow.d(32):        which wasn't inferred `nothrow` because of:
+fail_compilation/attributediagnostic_nothrow.d(32):        `object.Exception` is thrown but not caught
+fail_compilation/attributediagnostic_nothrow.d(44): Error: function `attributediagnostic_nothrow.gc2` is not `nothrow`
+fail_compilation/attributediagnostic_nothrow.d(41): Error: function `D main` may throw but is marked as `nothrow`
+---
+*/
+
+// Issue 17374 - Improve inferred attribute error message
+// https://issues.dlang.org/show_bug.cgi?id=17374
+
+auto layer2() nothrow { layer1(); }
+auto layer1() { layer0(); }
+auto layer0() { gc(); }
+
+auto gc()
+{
+    asm {}
+}
+
+auto gc1()
+{
+    throw new Exception("msg");
+}
+
+auto fgc = function void() {throw new Exception("msg");};
+auto gc2()
+{
+    fgc();
+}
+
+void main() nothrow
+{
+    gc1();
+    gc2();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_pure.d b/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic_pure.d
new file mode 100644 (file)
index 0000000..a120dab
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/attributediagnostic_pure.d(20): Error: `pure` function `D main` cannot call impure function `attributediagnostic_pure.gc`
+fail_compilation/attributediagnostic_pure.d(15):        which wasn't inferred `pure` because of:
+fail_compilation/attributediagnostic_pure.d(15):        `asm` statement is assumed to be impure - mark it with `pure` if it is not
+---
+*/
+
+// Issue 17374 - Improve inferred attribute error message
+// https://issues.dlang.org/show_bug.cgi?id=17374
+
+auto gc()
+{
+    asm {}
+}
+
+void main() pure
+{
+    gc();
+}
index c980d76a73de8c654e05c3243c3e7ad4c5ea1bef..802d1c2983eff42c4fb92149155c05d0c9431e9d 100644 (file)
@@ -66,8 +66,8 @@ fail_compilation/bug9631.d(79): Error: function `bug9631.arg.f(int i, S s)` is n
 fail_compilation/bug9631.d(79):        cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s`
 fail_compilation/bug9631.d(80): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)`
 fail_compilation/bug9631.d(80):        cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s`
-fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S _param_0)` is not callable using argument types `(S)`
-fail_compilation/bug9631.d(86):        cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S _param_0`
+fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)`
+fail_compilation/bug9631.d(86):        cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0`
 ---
 */
 void arg()
@@ -89,8 +89,8 @@ void arg()
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S _param_0)` is not callable using argument types `(S)`
-fail_compilation/bug9631.d(106):        cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S _param_0`
+fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S __param_0)` is not callable using argument types `(S)`
+fail_compilation/bug9631.d(106):        cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S __param_0`
 fail_compilation/bug9631.d(107): Error: none of the overloads of template `bug9631.targ.ft` are callable using argument types `!()(S)`
 fail_compilation/bug9631.d(105):        Candidate is: `ft()(tem!().S)`
 fail_compilation/bug9631.d(109): Error: none of the overloads of template `bug9631.targ.ft2` are callable using argument types `!()(S, int)`
index 2d8bf7a0fefa3327fe3f3c170f52e1eb18472faf..9c901033223ec0ddd6f4e59f9550baa756ce5122 100644 (file)
@@ -19,7 +19,7 @@ struct T { }
     {
 L1:
         new T();
-       a = 3;
+        a = 3;
     }
     goto L1;
 }
@@ -31,3 +31,14 @@ L1:
         new T();
     }
 }
+
+@nogc void test3()
+{
+    if (!__ctfe)
+    {
+    }
+    else
+    {
+        int* p = new int;
+    }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecatedinref.d b/gcc/testsuite/gdc.test/fail_compilation/deprecatedinref.d
new file mode 100644 (file)
index 0000000..20c3666
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/deprecatedinref.d(9): Deprecation: using `in ref` is deprecated, use `-preview=in` and `in` instead
+fail_compilation/deprecatedinref.d(10): Deprecation: using `ref in` is deprecated, use `-preview=in` and `in` instead
+---
+*/
+void foo(in ref int);
+void foor(ref in int);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecations_preview_in.d b/gcc/testsuite/gdc.test/fail_compilation/deprecations_preview_in.d
new file mode 100644 (file)
index 0000000..33cc904
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/deprecations_preview_in.d(1): Deprecation: using `in` parameters with `extern(C)` functions is deprecated
+fail_compilation/deprecations_preview_in.d(1):        parameter `__anonymous_param` declared as `in` here
+---
+*/
+
+#line 1
+extern(C) void fun1(in char*);
index 7b2eca79b80d74ffd9c4555a9c1acb858f0d28dd..efc818f30be5e5f1e77da1b743cb114bf2231c7d 100644 (file)
@@ -1,17 +1,21 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag10319.d(29): Error: `pure` function `D main` cannot call impure function `diag10319.foo`
-fail_compilation/diag10319.d(29): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo`
-fail_compilation/diag10319.d(18):        `diag10319.foo` is declared here
-fail_compilation/diag10319.d(30): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar`
-fail_compilation/diag10319.d(30): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar`
-fail_compilation/diag10319.d(23):        which was inferred `@system` because of:
-fail_compilation/diag10319.d(23):        cannot take address of local `x` in `@safe` function `bar`
-fail_compilation/diag10319.d(20):        `diag10319.bar!int.bar` is declared here
-fail_compilation/diag10319.d(29): Error: function `diag10319.foo` is not `nothrow`
-fail_compilation/diag10319.d(30): Error: function `diag10319.bar!int.bar` is not `nothrow`
-fail_compilation/diag10319.d(27): Error: function `D main` may throw but is marked as `nothrow`
+fail_compilation/diag10319.d(33): Error: `pure` function `D main` cannot call impure function `diag10319.foo`
+fail_compilation/diag10319.d(33): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo`
+fail_compilation/diag10319.d(22):        `diag10319.foo` is declared here
+fail_compilation/diag10319.d(34): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar`
+fail_compilation/diag10319.d(26):        which wasn't inferred `pure` because of:
+fail_compilation/diag10319.d(26):        `pure` function `diag10319.bar!int.bar` cannot access mutable static data `g`
+fail_compilation/diag10319.d(34): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar`
+fail_compilation/diag10319.d(27):        which wasn't inferred `@safe` because of:
+fail_compilation/diag10319.d(27):        cannot take address of local `x` in `@safe` function `bar`
+fail_compilation/diag10319.d(24):        `diag10319.bar!int.bar` is declared here
+fail_compilation/diag10319.d(33): Error: function `diag10319.foo` is not `nothrow`
+fail_compilation/diag10319.d(34): Error: function `diag10319.bar!int.bar` is not `nothrow`
+fail_compilation/diag10319.d(28):        which wasn't inferred `nothrow` because of:
+fail_compilation/diag10319.d(28):        `object.Exception` is thrown but not caught
+fail_compilation/diag10319.d(31): Error: function `D main` may throw but is marked as `nothrow`
 ---
 */
 
index 1fde171b71317a80d6454e39a47dd637f23f0c0a..207f6a4aa1508e2945284d3096d46713ef24bfe3 100644 (file)
@@ -3,7 +3,7 @@ TEST_OUTPUT:
 ---
 fail_compilation/diag10415.d(36): Error: none of the overloads of `x` are callable using argument types `(int) const`
 fail_compilation/diag10415.d(13):        Candidates are: `diag10415.C.x()`
-fail_compilation/diag10415.d(18):                        `diag10415.C.x(int _param_0)`
+fail_compilation/diag10415.d(18):                        `diag10415.C.x(int __param_0)`
 fail_compilation/diag10415.d(39): Error: d.x is not an lvalue
 ---
 */
index 2717de4a6e7f528230a05bbd237e092a5de40fc2..75047f537858b377ab8c55e3118ef1222bf96638 100644 (file)
@@ -2,9 +2,9 @@
 TEST_OUTPUT:
 ---
 fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches both:
-fail_compilation/diag11769.d(13):     `diag11769.foo!string.bar(wstring _param_0)`
+fail_compilation/diag11769.d(13):     `diag11769.foo!string.bar(wstring __param_0)`
 and:
-fail_compilation/diag11769.d(14):     `diag11769.foo!string.bar(dstring _param_0)`
+fail_compilation/diag11769.d(14):     `diag11769.foo!string.bar(dstring __param_0)`
 ---
 */
 
index f9b535ab8d70a7401f9d30a92dcb0496457f8b73..6147f32d0db106ffa4777c6d67972d80f7da472c 100644 (file)
@@ -2,8 +2,8 @@
 TEST_OUTPUT:
 ---
 fail_compilation/diag14818.d(40): Error: none of the overloads of `func` are callable using argument types `(string)`
-fail_compilation/diag14818.d(18):        Candidates are: `diag14818.foo(int _param_0)`
-fail_compilation/diag14818.d(19):                        `diag14818.bar(double _param_0)`
+fail_compilation/diag14818.d(18):        Candidates are: `diag14818.foo(int __param_0)`
+fail_compilation/diag14818.d(19):                        `diag14818.bar(double __param_0)`
 fail_compilation/diag14818.d(41): Error: template instance `diag14818.X!string` does not match any template declaration
 fail_compilation/diag14818.d(41):        Candidates are:
 fail_compilation/diag14818.d(24):        Foo(T) if (is(T == int))
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag20268.d b/gcc/testsuite/gdc.test/fail_compilation/diag20268.d
new file mode 100644 (file)
index 0000000..a314561
--- /dev/null
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=20268
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag20268.d(12): Error: none of the overloads of template `diag20268.__lambda4` are callable using argument types `!()(int)`
+fail_compilation/diag20268.d(11):        Candidate is: `__lambda4(__T1, __T2)(x, y)`
+---
+*/
+
+alias f = (x,y) => true;
+auto x = f(1);
index bc0ee9d2fb7015ce269cc9c828ad62ce418cec9e..a55ef731ad2b2cb06f9c00704dd7ddf5f0c0f16d 100644 (file)
@@ -2,13 +2,13 @@
 TEST_OUTPUT:
 ---
 fail_compilation/diag8101b.d(28): Error: none of the overloads of `foo` are callable using argument types `(double)`
-fail_compilation/diag8101b.d(19):        Candidates are: `diag8101b.S.foo(int _param_0)`
-fail_compilation/diag8101b.d(20):                        `diag8101b.S.foo(int _param_0, int _param_1)`
-fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)`
-fail_compilation/diag8101b.d(30):        cannot pass argument `1.0` of type `double` to parameter `int _param_0`
+fail_compilation/diag8101b.d(19):        Candidates are: `diag8101b.S.foo(int __param_0)`
+fail_compilation/diag8101b.d(20):                        `diag8101b.S.foo(int __param_0, int __param_1)`
+fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int __param_0)` is not callable using argument types `(double)`
+fail_compilation/diag8101b.d(30):        cannot pass argument `1.0` of type `double` to parameter `int __param_0`
 fail_compilation/diag8101b.d(33): Error: none of the overloads of `foo` are callable using a `const` object
-fail_compilation/diag8101b.d(19):        Candidates are: `diag8101b.S.foo(int _param_0)`
-fail_compilation/diag8101b.d(20):                        `diag8101b.S.foo(int _param_0, int _param_1)`
+fail_compilation/diag8101b.d(19):        Candidates are: `diag8101b.S.foo(int __param_0)`
+fail_compilation/diag8101b.d(20):                        `diag8101b.S.foo(int __param_0, int __param_1)`
 fail_compilation/diag8101b.d(35): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object
 fail_compilation/diag8101b.d(22):        Consider adding `const` or `inout` here
 ---
index 94e3d3ffd433428a5f5118e43b151233faf0cf3b..98308133e02d15421dc4657c8538ff224a1267a5 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag9312.d(10): Error: `with` expressions must be aggregate types or pointers to them, not `int`
+fail_compilation/diag9312.d(10): Error: `with` expression types must be enums or aggregates or pointers to them, not `int`
 ---
 */
 
index d99290c6a394f1c3486ee66abcc56a996e24f87a..4af87df0a2f745cce823adf7586879d03729c506 100644 (file)
@@ -1,8 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag9620.d(18): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1`
-fail_compilation/diag9620.d(19): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2`
+fail_compilation/diag9620.d(20): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1`
+fail_compilation/diag9620.d(21): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2`
+fail_compilation/diag9620.d(14):        which wasn't inferred `pure` because of:
+fail_compilation/diag9620.d(14):        `pure` function `diag9620.foo2!().foo2` cannot access mutable static data `x`
 ---
 */
 
index b990ced0522fdb42b5ac7d458e4a5ea7e3ed4c6e..c93a06a465ac85fe921d09d60276b344e8c98e8f 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3` cannot access variable `c` in frame of function `D main`
+fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3(__T1)(x)` cannot access variable `c` in frame of function `D main`
 fail_compilation/diag9831.d(11):        `c` declared here
 ---
 */
index bf51363c07eff07e8d958adb2fb8ae1fad456804..61174395be32a7033d0df09d003decba7d986e9b 100644 (file)
@@ -3,11 +3,11 @@ REQUIRED_ARGS: -de
 TEST_OUTPUT:
 ---
 fail_compilation/dip1000_deprecation.d(20): Deprecation: `@safe` function `main` calling `inferred`
-fail_compilation/dip1000_deprecation.d(28):        which would be `@system` because of:
+fail_compilation/dip1000_deprecation.d(28):        which wouldn't be `@safe` because of:
 fail_compilation/dip1000_deprecation.d(28):        scope variable `x0` may not be returned
 fail_compilation/dip1000_deprecation.d(22): Deprecation: `@safe` function `main` calling `inferredC`
 fail_compilation/dip1000_deprecation.d(39):        which calls `dip1000_deprecation.inferred`
-fail_compilation/dip1000_deprecation.d(28):        which would be `@system` because of:
+fail_compilation/dip1000_deprecation.d(28):        which wouldn't be `@safe` because of:
 fail_compilation/dip1000_deprecation.d(28):        scope variable `x0` may not be returned
 fail_compilation/dip1000_deprecation.d(54): Deprecation: escaping reference to stack allocated value returned by `S(null)`
 fail_compilation/dip1000_deprecation.d(55): Deprecation: escaping reference to stack allocated value returned by `createS()`
index ce81d6bfd3552e2d77fb89edc1122266f856739a..21a12ed0253cc99d735102d36aeb8e81c59cc808 100644 (file)
@@ -8,8 +8,6 @@ fail_compilation/dtor_attributes.d(113):        generated `Strict.~this` is impu
 fail_compilation/dtor_attributes.d(111):         - HasDtor member
 fail_compilation/dtor_attributes.d(103):           impure `HasDtor.~this` is declared here
 fail_compilation/dtor_attributes.d(118): Error: `@safe` function `dtor_attributes.test1` cannot call `@system` destructor `dtor_attributes.Strict.~this`
-fail_compilation/dtor_attributes.d(113):        which calls `dtor_attributes.Strict.~this`
-fail_compilation/dtor_attributes.d(103):        which calls `dtor_attributes.HasDtor.~this`
 fail_compilation/dtor_attributes.d(113):        `dtor_attributes.Strict.~this` is declared here
 fail_compilation/dtor_attributes.d(113):        generated `Strict.~this` is @system because of the following field's destructors:
 fail_compilation/dtor_attributes.d(111):         - HasDtor member
index 45b23cece4dab384ceef2b7bc352117869022ff4..f6cab893bb44e222a769954c1804b2a9ad09d26a 100644 (file)
@@ -9,7 +9,6 @@ fail_compilation/dtorfields_attributes.d(119):        generated `Strict.~this` i
 fail_compilation/dtorfields_attributes.d(115):         - HasDtor member
 fail_compilation/dtorfields_attributes.d(103):           impure `HasDtor.~this` is declared here
 fail_compilation/dtorfields_attributes.d(117): Error: `@safe` constructor `dtorfields_attributes.Strict.this` cannot call `@system` destructor `dtorfields_attributes.Strict.~this`
-fail_compilation/dtorfields_attributes.d(103):        which calls `dtorfields_attributes.HasDtor.~this`
 fail_compilation/dtorfields_attributes.d(119):        `dtorfields_attributes.Strict.~this` is declared here
 fail_compilation/dtorfields_attributes.d(119):        generated `Strict.~this` is @system because of the following field's destructors:
 fail_compilation/dtorfields_attributes.d(115):         - HasDtor member
index cfda8f4d9ad00a3fd1b653bc2ba079c9575cb074..a436197d0477e3d44c83ec424662546f0213453b 100644 (file)
@@ -8,10 +8,14 @@ fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot
 fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(31):        `fail10968.SA.__postblit` is declared here
 fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arraysetassign!(SA[], SA)._d_arraysetassign`
+$p:druntime/import/core/internal/array/arrayassign.d$($n$):        which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace`
+$p:druntime/import/core/lifetime.d$($n$):        which calls `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(31):        `fail10968.SA.__postblit` is declared here
 fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arrayassign_l!(SA[], SA)._d_arrayassign_l`
+$p:druntime/import/core/internal/array/arrayassign.d$-mixin-$n$($n$):        which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace`
+$p:druntime/import/core/lifetime.d$($n$):        which calls `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(31):        `fail10968.SA.__postblit` is declared here
@@ -19,13 +23,18 @@ fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot
 fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(31):        `fail10968.SA.__postblit` is declared here
 fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor`
+$p:druntime/import/core/internal/array/construction.d$($n$):        which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace`
+$p:druntime/import/core/lifetime.d$($n$):        which calls `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(50): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(31):        `fail10968.SA.__postblit` is declared here
 fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor`
+$p:druntime/import/core/internal/array/construction.d$($n$):        which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace`
+$p:druntime/import/core/lifetime.d$($n$):        which calls `fail10968.SA.__postblit`
 ---
 */
 
+#line 29
 struct SA
 {
     this(this)
index 7592a5a1dfd23a6bb88a4ab22389f6e27217bc52..cabf87a6cae3463f747d97ae920b17e5d426bb81 100644 (file)
@@ -1,8 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail11375.d(17): Error: constructor `fail11375.D!().D.this` is not `nothrow`
-fail_compilation/fail11375.d(15): Error: function `D main` may throw but is marked as `nothrow`
+fail_compilation/fail11375.d(18): Error: constructor `fail11375.D!().D.this` is not `nothrow`
+       which calls `fail11375.B.this`
+fail_compilation/fail11375.d(16): Error: function `D main` may throw but is marked as `nothrow`
 ---
 */
 
index 738864cca35bd402516e4b483efcc3fe8bd891b8..824f5e48db2590d4dd305bf97c3a74b4f47f7adc 100644 (file)
@@ -7,7 +7,7 @@ fail_compilation/fail12236.d(21): Error: forward reference to inferred return ty
 fail_compilation/fail12236.d(21):        while evaluating `pragma(msg, f2(T)(T).mangleof)`
 fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating
 fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1`
-fail_compilation/fail12236.d(31):        while evaluating `pragma(msg, __lambda1.mangleof)`
+fail_compilation/fail12236.d(31):        while evaluating `pragma(msg, __lambda1(__T1)(a).mangleof)`
 ---
 */
 
index f1cf340b6a0d4b5938b43fd1a1cfe7066921e3dc..6a1335eebd5b006ee8ad6345e31a018c19dc095a 100644 (file)
@@ -17,13 +17,13 @@ void g1(char[] s) pure @nogc
 TEST_OUTPUT:
 ---
 fail_compilation/fail13120.d(35): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2`
+fail_compilation/fail13120.d(30):        which calls `fail13120.f2`
 fail_compilation/fail13120.d(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2`
 fail_compilation/fail13120.d(27):        `fail13120.g2!().g2` is declared here
 fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2`
 ---
 */
 void f2() {}
-
 void g2()(char[] s)
 {
     foreach (dchar dc; s)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13577.d b/gcc/testsuite/gdc.test/fail_compilation/fail13577.d
new file mode 100644 (file)
index 0000000..79f9068
--- /dev/null
@@ -0,0 +1,28 @@
+// https://issues.dlang.org/show_bug.cgi?id=13577
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail13577.d(27): Error: cannot implicilty convert range element of type `int[]` to variable `x` of type `immutable(int[])`
+---
+*/
+
+struct Tuple(Types...)
+{
+    Types items;
+    alias items this;
+}
+
+struct Range(T)
+{
+    T[] arr;
+    alias ElemType = Tuple!(int, T);
+    ElemType front() { return typeof(return)(0, arr[0]); }
+    bool empty() { return false; }
+    void popFront() {}
+}
+
+void main()
+{
+    foreach (immutable i, immutable x; Range!(int[])()) {} // Error
+}
index eb341c64af5f86277a7b0aa49be350b4c7a7039c..3bd600e507b866bcf3a3935d8c6947d63cd28178 100644 (file)
@@ -1,9 +1,9 @@
 /* TEST_OUTPUT:
 ---
 fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches both:
-fail_compilation/fail16600.d(16):     `fail16600.S.this(string _param_0)`
+fail_compilation/fail16600.d(16):     `fail16600.S.this(string __param_0)`
 and:
-fail_compilation/fail16600.d(17):     `fail16600.S.this(string _param_0) immutable`
+fail_compilation/fail16600.d(17):     `fail16600.S.this(string __param_0) immutable`
 ---
 */
 
index 385483c048dd1a199e0f54c3f59e52c4d1f1063f..cf2648db40bf157043e301f737eb03dde92eedc0 100644 (file)
@@ -1,8 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) _param_0) inout` is not callable using argument types `(Wrong)`
-fail_compilation/fail17518.d(21):        cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) _param_0`
+fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) __param_0) inout` is not callable using argument types `(Wrong)`
+fail_compilation/fail17518.d(21):        cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) __param_0`
 ---
 */
 
index 95eb5cc8c1f92639456752d5e91b8f7835a5ae5d..08329194228851c28d318178c75f71f5d0af31dc 100644 (file)
@@ -11,7 +11,7 @@ fail_compilation/fail17955.d(49):        instantiated from here: `toRedis!(SysTi
 fail_compilation/fail17955.d(40):        ... (2 instantiations, -v to show) ...
 fail_compilation/fail17955.d(32):        instantiated from here: `indicesOf!(isRedisType, resetCodeExpireTime)`
 fail_compilation/fail17955.d(67):        instantiated from here: `RedisStripped!(User, true)`
-fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring _param_0)`
+fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring __param_0)`
 fail_compilation/fail17955.d(95): Error: undefined identifier `DateTimeException`
 fail_compilation/fail17955.d(25): Error: variable `fail17955.isISOExtStringSerializable!(SysTime).isISOExtStringSerializable` - type `void` is inferred from initializer `fromISOExtString("")`, and variables cannot be of type `void`
 fail_compilation/fail17955.d(54): Error: function `fail17955.toRedis!(SysTime).toRedis` has no `return` statement, but is expected to return a value of type `string`
index 2c7d93fe4e064105d9bcab92a282bd35ccfe9753..53505f496c2279ecad520ea447b6ed5d10fa3ac3 100644 (file)
@@ -6,15 +6,15 @@ fail_compilation/fail196.d(27): Error: implicit string concatenation is error-pr
 fail_compilation/fail196.d(27):        Use the explicit syntax instead (concatenating literals is `@nogc`): "foo(xxx)" ~ ";\n    assert(s == "
 fail_compilation/fail196.d(28): Error: semicolon needed to end declaration of `s`, instead of `foo`
 fail_compilation/fail196.d(27):        `s` declared here
-fail_compilation/fail196.d(28): Error: found `");\n\n    s = q"` when expecting `;` following statement
-fail_compilation/fail196.d(30): Error: found `";\n    assert(s == "` when expecting `;` following statement
-fail_compilation/fail196.d(31): Error: found `");\n\n    s = q"` when expecting `;` following statement
-fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement
-fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement
-fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement
-fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement
-fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement
-fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement
+fail_compilation/fail196.d(28): Error: found `");\n\n    s = q"` when expecting `;` following statement `foo(xxx)` on line fail_compilation/fail196.d(28)
+fail_compilation/fail196.d(30): Error: found `";\n    assert(s == "` when expecting `;` following statement `[foo[xxx]]` on line fail_compilation/fail196.d(30)
+fail_compilation/fail196.d(31): Error: found `");\n\n    s = q"` when expecting `;` following statement `foo[xxx]` on line fail_compilation/fail196.d(31)
+fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement `foo` on line fail_compilation/fail196.d(33)
+fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(33)
+fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement `";\n    assert(s == "` on line fail_compilation/fail196.d(33)
+fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(34)
+fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement `");\n\n    s = q" < foo` on line fail_compilation/fail196.d(34)
+fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement `xxx >> ";\n    assert(s == "` on line fail_compilation/fail196.d(36)
 fail_compilation/fail196.d(37): Error: found `<` instead of statement
 fail_compilation/fail196.d(43): Error: unterminated string constant starting at fail_compilation/fail196.d(43)
 fail_compilation/fail196.d(45): Error: found `End of File` when expecting `}` following compound statement
index e8a9e77790455785d450f6a229cdb80212c8d3d9..ae67443fea437c401aedfa280c583a18e07e4759 100644 (file)
@@ -1,5 +1,5 @@
 // https://issues.dlang.org/show_bug.cgi?id=19948
-
+// DISABLED: win32
 /*
 TEST_OUTPUT:
 ---
index 05b7c85375a7b88fedfacb8829e67f0c846da73a..80a5d4615741e15e411b3c8fbd93de8569d16f84 100644 (file)
@@ -4,7 +4,7 @@
 fail_compilation/fail20609.d(26): Error: none of the overloads of `this` are callable using argument types `(int)`
 fail_compilation/fail20609.d(23):        Candidate is: `fail20609.Foo.this(string[] args)`
 fail_compilation/fail20609.d(27): Error: none of the overloads of `this` are callable using argument types `(int)`
-fail_compilation/fail20609.d(22):        Candidates are: `fail20609.Foo.this(Object _param_0)`
+fail_compilation/fail20609.d(22):        Candidates are: `fail20609.Foo.this(Object __param_0)`
 fail_compilation/fail20609.d(23):                        `fail20609.Foo.this(string[] args)`
 fail_compilation/fail20609.d(37): Error: none of the overloads of `this` are callable using argument types `(int)`
 fail_compilation/fail20609.d(37):        All possible candidates are marked as `deprecated` or `@disable`
index 167d362487936b7a5116de7e3ec47eefaf099d77..d865fd95553af8786aa1872432e3f5901de0c5bb 100644 (file)
@@ -3,7 +3,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy _param_0)` is not callable using argument types `(SystemCopy)`
+fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy __param_0)` is not callable using argument types `(SystemCopy)`
 fail_compilation/fail22202.d(21):        `inout ref inout(SystemCopy)(ref inout(SystemCopy) other)` copy constructor cannot be called from a `pure @safe nogc` context
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23773.d b/gcc/testsuite/gdc.test/fail_compilation/fail23773.d
new file mode 100644 (file)
index 0000000..e6cdc3e
--- /dev/null
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=23773
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail23773.d(14): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+---
+*/
+
+void main()
+{
+    int i;
+    int[] arr;
+    assert(arr.length = i);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23822.d b/gcc/testsuite/gdc.test/fail_compilation/fail23822.d
new file mode 100644 (file)
index 0000000..5cdd1fe
--- /dev/null
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=23822
+
+// REQUIRED_ARGS: -de
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail23822.d(21): Deprecation: alias `fail23822.S.value` is deprecated
+---
+*/
+
+alias Alias(alias A) = A;
+
+struct S
+{
+    deprecated alias value = Alias!5;
+}
+
+void main()
+{
+    auto a = S.value;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23826.d b/gcc/testsuite/gdc.test/fail_compilation/fail23826.d
new file mode 100644 (file)
index 0000000..3db243a
--- /dev/null
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=23826
+
+// REQUIRED_ARGS: -de
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail23826.d(23): Deprecation: alias `fail23826.S.value` is deprecated
+---
+*/
+
+alias Alias(alias A) = A;
+
+class S
+{
+    deprecated alias value = Alias!5;
+}
+
+enum identity(alias A) = A;
+
+void main()
+{
+    auto a = identity!(S.value);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23861.d b/gcc/testsuite/gdc.test/fail_compilation/fail23861.d
new file mode 100644 (file)
index 0000000..23c5407
--- /dev/null
@@ -0,0 +1,25 @@
+// https://issues.dlang.org/show_bug.cgi?id=23861
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail23861.d(24): Error: cannot implicitly convert expression `3` of type `int` to `Foo`
+---
+*/
+
+Foo global;
+
+struct Foo
+{
+    ref Foo get()
+    {
+        return global;
+    }
+    alias get this;
+}
+
+void main()
+{
+    Foo g;
+    g = 3;
+}
index 91f8046470564ed893e1a25eb3e2e205b8fb71cf..77e8cd8eb00e722366c03bfc006e5fd78d7c1b00 100644 (file)
@@ -1,14 +1,14 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail332.d(22): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `()`
-fail_compilation/fail332.d(22):        missing argument for parameter #1: `int _param_0`
-fail_compilation/fail332.d(23): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `(typeof(null))`
-fail_compilation/fail332.d(23):        cannot pass argument `null` of type `typeof(null)` to parameter `int _param_0`
-fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(string)`
-fail_compilation/fail332.d(25):        cannot pass argument `""` of type `string` to parameter `int[] _param_0...`
-fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(int, typeof(null))`
-fail_compilation/fail332.d(26):        cannot pass argument `null` of type `typeof(null)` to parameter `int[] _param_0...`
+fail_compilation/fail332.d(22): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `()`
+fail_compilation/fail332.d(22):        missing argument for parameter #1: `int __param_0`
+fail_compilation/fail332.d(23): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `(typeof(null))`
+fail_compilation/fail332.d(23):        cannot pass argument `null` of type `typeof(null)` to parameter `int __param_0`
+fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(string)`
+fail_compilation/fail332.d(25):        cannot pass argument `""` of type `string` to parameter `int[] __param_0...`
+fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(int, typeof(null))`
+fail_compilation/fail332.d(26):        cannot pass argument `null` of type `typeof(null)` to parameter `int[] __param_0...`
 ---
 */
 
index b02fbb169b749dfcde62f5023aa43b2dc0f7502d..f57e746b6ed33e7a9215c4a196bf61f32d57c7cd 100644 (file)
@@ -4,7 +4,7 @@
 TEST_OUTPUT:
 ---
 fail_compilation/fail4375q.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375q.d(13)
-fail_compilation/fail4375q.d(14): Error: `with` expressions must be aggregate types or pointers to them, not `int`
+fail_compilation/fail4375q.d(14): Error: `with` expression types must be enums or aggregates or pointers to them, not `int`
 ---
 */
 
index 0101ae98bf1a093eedb71bf1aa0bbbc2a719d2ea..657c184d7875911368c42d847aab0646898db06c 100644 (file)
@@ -1,10 +1,10 @@
 /*
-REQUIRED_ARGS: -o- -de
+REQUIRED_ARGS:
 TEST_OUTPUT:
 ---
-fail_compilation/fail4559.d(13): Deprecation: use `{ }` for an empty statement, not `;`
-fail_compilation/fail4559.d(19): Deprecation: use `{ }` for an empty statement, not `;`
-fail_compilation/fail4559.d(21): Deprecation: use `{ }` for an empty statement, not `;`
+fail_compilation/fail4559.d(13): Error: use `{ }` for an empty statement, not `;`
+fail_compilation/fail4559.d(19): Error: use `{ }` for an empty statement, not `;`
+fail_compilation/fail4559.d(21): Error: use `{ }` for an empty statement, not `;`
 ---
 */
 
index 392cebd2f73e8b54cb7ee7698bc12755f098a668..a3b4d1a1415388fed58624d5a938596479781e69 100644 (file)
@@ -1,17 +1,14 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/fail_typeof.d(18): Error: undefined identifier `this`
-fail_compilation/fail_typeof.d(23): Error: `this` is not in a class or struct scope
-fail_compilation/fail_typeof.d(23): Error: `this` is only defined in non-static member functions, not `fail_typeof`
-fail_compilation/fail_typeof.d(28): Error: undefined identifier `super`
-fail_compilation/fail_typeof.d(33): Error: `super` is not in a class scope
-fail_compilation/fail_typeof.d(33): Error: `super` is only allowed in non-static class member functions
-fail_compilation/fail_typeof.d(40): Error: undefined identifier `this`, did you mean `typeof(this)`?
-fail_compilation/fail_typeof.d(50): Error: undefined identifier `super`
-fail_compilation/fail_typeof.d(55): Error: `super` is not in a class scope
-fail_compilation/fail_typeof.d(55): Error: `super` is only allowed in non-static class member functions
-fail_compilation/fail_typeof.d(63): Error: undefined identifier `this`, did you mean `typeof(this)`?
-fail_compilation/fail_typeof.d(73): Error: undefined identifier `super`, did you mean `typeof(super)`?
+fail_compilation/fail_typeof.d(15): Error: undefined identifier `this`
+fail_compilation/fail_typeof.d(20): Error: `this` is not in a class or struct scope
+fail_compilation/fail_typeof.d(25): Error: undefined identifier `super`
+fail_compilation/fail_typeof.d(30): Error: `super` is not in a class scope
+fail_compilation/fail_typeof.d(37): Error: undefined identifier `this`, did you mean `typeof(this)`?
+fail_compilation/fail_typeof.d(47): Error: undefined identifier `super`
+fail_compilation/fail_typeof.d(52): Error: `super` is not in a class scope
+fail_compilation/fail_typeof.d(60): Error: undefined identifier `this`, did you mean `typeof(this)`?
+fail_compilation/fail_typeof.d(70): Error: undefined identifier `super`, did you mean `typeof(super)`?
 ---
 */
 
index 1f87955b95990287fe68ab426db4b79674fbf9fa..8b6c7200bb72ac61e57d886f917ef76b941bc1ab 100644 (file)
@@ -1,7 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice10651.d(11): Error: can only throw class objects derived from `Throwable`, not type `int*`
+fail_compilation/ice10651.d(13): Error: can only throw class objects derived from `Throwable`, not type `int*`
+fail_compilation/ice10651.d(19): Deprecation: cannot throw object of qualified type `immutable(Exception)`
+fail_compilation/ice10651.d(20): Deprecation: cannot throw object of qualified type `const(Dummy)`
 ---
 */
 
@@ -10,3 +12,20 @@ void main()
     alias T = int;
     throw new T();  // ICE
 }
+
+void f()
+{
+    immutable c = new Exception("");
+    if (c) throw c;
+    throw new const Dummy([]);
+}
+
+class Dummy: Exception
+{
+    int[] data;
+    @safe pure nothrow this(immutable int[] data) immutable
+    {
+        super("Dummy");
+        this.data = data;
+    }
+}
index 5dc5d5c1e6619d2e24c867b4c39150c4691ae841..6d347bcdd0e1c3d549c26fa282a96dc993d2c111 100644 (file)
@@ -5,4 +5,4 @@ fail_compilation/ice11626.d(8): Error: undefined identifier `Bar`
 ---
 */
 
-void foo(in ref Bar) {}
+void foo(const ref Bar) {}
index ff5fae491c60b08f78a4b931d89ffea8f5b30847..0f2ce413c40eff5d8a10c6a145c40166f05666dc 100644 (file)
@@ -1,16 +1,19 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice11982.d(16): Error: basic type expected, not `scope`
-fail_compilation/ice11982.d(16): Error: found `scope` when expecting `;` following statement
-fail_compilation/ice11982.d(16): Error: basic type expected, not `}`
-fail_compilation/ice11982.d(16): Error: missing `{ ... }` for function literal
-fail_compilation/ice11982.d(16): Error: C style cast illegal, use `cast(funk)function _error_()
+fail_compilation/ice11982.d(19): Error: basic type expected, not `scope`
+fail_compilation/ice11982.d(19): Error: found `scope` when expecting `;` following statement `new _error_` on line fail_compilation/ice11982.d(19)
+fail_compilation/ice11982.d(19): Error: basic type expected, not `}`
+fail_compilation/ice11982.d(19): Error: missing `{ ... }` for function literal
+fail_compilation/ice11982.d(19): Error: C style cast illegal, use `cast(funk)function _error_()
 {
 }
 `
-fail_compilation/ice11982.d(16): Error: found `}` when expecting `;` following statement
-fail_compilation/ice11982.d(17): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/ice11982.d(19): Error: found `}` when expecting `;` following statement `cast(funk)function _error_()
+{
+}
+` on line fail_compilation/ice11982.d(19)
+fail_compilation/ice11982.d(20): Error: found `End of File` when expecting `}` following compound statement
 ---
 */
 void main() { new scope ( funk ) function }
index 6988cd7c18d7a27b4f2752ea7045c070d82f5d2b..abc30eaf4f2b7d71a5250687388364ecdca8e8c6 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S _param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)`
+fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S __param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)`
 fail_compilation/ice13225.d(16): Error: undefined identifier `undefined`
 ---
 */
index 4fd1f61f82892da82e5df305e000cdc9aae2218b..90cf03f1b48d7da94b017d1d9c696162ee05764e 100644 (file)
@@ -3,7 +3,7 @@ TEST_OUTPUT:
 ---
 fail_compilation/ice23097.d(12): Error: undefined identifier `ICE`
 fail_compilation/ice23097.d(27): Error: template instance `ice23097.ice23097!(S23097)` error instantiating
-fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 _param_0)` is not callable using argument types `(S23097)`
+fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 __param_0)` is not callable using argument types `(S23097)`
 fail_compilation/ice23097.d(27):        generating a copy constructor for `struct S23097` failed, therefore instances of it are uncopyable
 ---
 */
index 5276e83c72294caedd840b170ae5989e3114060d..456d3e12f236cea486a14d578efeb540fc7313e3 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int _param_0)` is not callable using argument types `()`
+fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int __param_0)` is not callable using argument types `()`
 fail_compilation/ice9540.d(35):        too few arguments, expected 1, got 0
 fail_compilation/ice9540.d(26): Error: template instance `ice9540.A.test.AddFront!(this, f)` error instantiating
 ---
index 11fddf069d6df4457b88ce4bbcdd713c505ade64..d13614409209627f428eeafdf283edee9edfaa7f 100644 (file)
@@ -23,7 +23,7 @@ fail_compilation/misc_parser_err_cov1.d(40): Error: semicolon expected following
 fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected following `.`, not `+`
 fail_compilation/misc_parser_err_cov1.d(41): Error: identifier or new keyword expected following `(...)`.
 fail_compilation/misc_parser_err_cov1.d(41): Error: expression expected, not `;`
-fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement
+fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement `(__error) + 0` on line fail_compilation/misc_parser_err_cov1.d(41)
 fail_compilation/misc_parser_err_cov1.d(43): Error: found `End of File` when expecting `}` following compound statement
 ---
 */
index 19e230ee519dc1c12907c6666fcea0c0ef21fb02..096c499790e605211626092c13ddafac6a1e452f 100644 (file)
@@ -1,13 +1,13 @@
 /**
 TEST_OUTPUT:
 ---
-fail_compilation/named_arguments_parse.d(10): Error: named arguments not allowed here
 fail_compilation/named_arguments_parse.d(13): Error: named arguments not allowed here
 fail_compilation/named_arguments_parse.d(14): Error: named arguments not allowed here
 ---
 */
 
-@(attribute: 3)
+
+// @(attribute: 3) Currently gives an ugly parse error, will be better when named template arguments are implemented
 void main()
 {
        mixin(thecode: "{}");
index c9c4288243809d1d9d82be4a905519f62f456e08..d13006db0ab25321a1b9c7951b4f76ca0467d32b 100644 (file)
@@ -3,7 +3,7 @@ TEST_OUTPUT:
 ---
 fail_compilation/parseStc.d(12): Error: missing closing `)` after `if (x`
 fail_compilation/parseStc.d(12): Error: use `{ }` for an empty statement, not `;`
-fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement
+fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement `1` on line fail_compilation/parseStc.d(12)
 fail_compilation/parseStc.d(13): Error: redundant attribute `const`
 ---
 */
index ca5409301291457a4417ffd23614d9c27664db36..d0e97c8bcd3f355272332e921062eab3ba5197d4 100644 (file)
@@ -4,10 +4,10 @@ TEST_OUTPUT:
 ---
 fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)`
 fail_compilation/previewin.d(4):        cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
-fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)`
-fail_compilation/previewin.d(5):        cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
-fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)`
-fail_compilation/previewin.d(6):        cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)`
+fail_compilation/previewin.d(5):        cannot pass argument `__lambda2` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)`
+fail_compilation/previewin.d(6):        cannot pass argument `__lambda3` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
 fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal`
 fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal`
 fail_compilation/previewin.d(17): Error: scope parameter `arg` may not be returned
index 829fb6a19709a3ed8f4373cd9382c1aba53cc3f6..2e7940f70fe8412cbfb81f939c7b36eeb74a3806 100644 (file)
@@ -86,8 +86,8 @@ fail_compilation/retscope2.d(504): Error: scope variable `c` may not be returned
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope2.d(604): Error: scope variable `_param_0` assigned to non-scope anonymous parameter calling `foo600`
-fail_compilation/retscope2.d(604): Error: scope variable `_param_1` assigned to non-scope anonymous parameter calling `foo600`
+fail_compilation/retscope2.d(604): Error: scope variable `__param_0` assigned to non-scope anonymous parameter calling `foo600`
+fail_compilation/retscope2.d(604): Error: scope variable `__param_1` assigned to non-scope anonymous parameter calling `foo600`
 fail_compilation/retscope2.d(614): Error: template instance `retscope2.test600!(int*, int*)` error instantiating
 ---
 */
index 5c581d1db71c2f1dcbc800cf061e1c362236cbbe..ddeae81bc23efa7287d67e84ccf958227283bdeb 100644 (file)
@@ -25,7 +25,7 @@ int* test() @safe
 ---
 fail_compilation/retscope6.d(7034): Error: address of variable `i` assigned to `s` with longer lifetime
 fail_compilation/retscope6.d(7035): Error: address of variable `i` assigned to `s` with longer lifetime
-fail_compilation/retscope6.d(7025): Error: scope variable `_param_2` assigned to `ref` variable `t` with longer lifetime
+fail_compilation/retscope6.d(7025): Error: scope variable `__param_2` assigned to `ref` variable `t` with longer lifetime
 fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating
 fail_compilation/retscope6.d(7037): Error: address of variable `i` assigned to `s` with longer lifetime
 ---
index 75dbe2dc1a09b4706fa066d2918959a33bddc779..b5115351efebc496fa389a936d86f5b2f8786c62 100644 (file)
@@ -4,7 +4,7 @@ TEST_OUTPUT:
 ---
 fail_compilation/systemvariables_deprecation.d(16): Deprecation: `@safe` function `main` calling `middle`
 fail_compilation/systemvariables_deprecation.d(21):        which calls `systemvariables_deprecation.inferred`
-fail_compilation/systemvariables_deprecation.d(27):        which would be `@system` because of:
+fail_compilation/systemvariables_deprecation.d(27):        which wouldn't be `@safe` because of:
 fail_compilation/systemvariables_deprecation.d(27):        cannot access `@system` variable `x0` in @safe code
 ---
 */
index c0d5a05d05cc305728d1d155823b71747b4fa581..145fc9e8b9d6cb19353e3efa52cf861e7483a18b 100644 (file)
@@ -138,8 +138,13 @@ immutable(void)* g10063(inout int* p) pure
 TEST_OUTPUT:
 ---
 fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049`
+fail_compilation/testInference.d(149):        which calls `testInference.foo14049!int.foo14049.__lambda2`
+fail_compilation/testInference.d(148):        which calls `testInference.impure14049`
+fail_compilation/testInference.d(143):        which wasn't inferred `pure` because of:
+fail_compilation/testInference.d(143):        `pure` function `testInference.impure14049` cannot access mutable static data `i`
 ---
 */
+#line 143
 auto impure14049() { static int i = 1; return i; }
 
 void foo14049(T)(T val)
@@ -170,8 +175,10 @@ int* f14160() pure
 TEST_OUTPUT:
 ---
 fail_compilation/testInference.d(180): Error: `pure` function `testInference.test12422` cannot call impure function `testInference.test12422.bar12422!().bar12422`
+fail_compilation/testInference.d(179):        which calls `testInference.foo12422`
 ---
 */
+#line 175
 int g12422;
 void foo12422() { ++g12422; }
 void test12422() pure
@@ -184,9 +191,15 @@ void test12422() pure
 TEST_OUTPUT:
 ---
 fail_compilation/testInference.d(198): Error: `pure` function `testInference.test13729a` cannot call impure function `testInference.test13729a.foo`
+fail_compilation/testInference.d(196):        which wasn't inferred `pure` because of:
+fail_compilation/testInference.d(196):        `pure` function `testInference.test13729a.foo` cannot access mutable static data `g13729`
 fail_compilation/testInference.d(206): Error: `pure` function `testInference.test13729b` cannot call impure function `testInference.test13729b.foo!().foo`
+fail_compilation/testInference.d(204):        which wasn't inferred `pure` because of:
+fail_compilation/testInference.d(204):        `pure` function `testInference.test13729b.foo!().foo` cannot access mutable static data `g13729`
 ---
 */
+
+#line 190
 int g13729;
 
 void test13729a() pure
@@ -229,8 +242,10 @@ void test17086_call ()
 TEST_OUTPUT:
 ---
 fail_compilation/testInference.d(238): Error: `pure` function `testInference.test20047_pure_function` cannot call impure function `testInference.test20047_pure_function.bug`
+fail_compilation/testInference.d(237):        which calls `testInference.test20047_impure_function`
 ---
 */
+#line 234
 void test20047_impure_function() {}
 void test20047_pure_function() pure
 {
index 96511f511e4c23f97ed15f2fc1842ee0b61ae99d..50cebabfda4332e17f919c8e6575e63d12899766 100644 (file)
@@ -6,7 +6,7 @@ TEST_OUTPUT:
 fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo`
 fail_compilation/testrvaluecpctor.d(24):        Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo`
 fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `__ctor` are callable using a `immutable` object
-fail_compilation/testrvaluecpctor.d(18):        Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref Foo!int rhs)`
+fail_compilation/testrvaluecpctor.d(18):        Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)`
 fail_compilation/testrvaluecpctor.d(16):                        `__ctor(Rhs, this This)(scope Rhs rhs)`
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d b/gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d
new file mode 100644 (file)
index 0000000..8609d29
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda8` of type `void function() nothrow @nogc @safe` to `void function() pure`
+---
+*/
+
+// Test the effect of function attributes on variables
+// See:
+//   https://issues.dlang.org/show_bug.cgi?id=7432
+//   https://github.com/dlang/dmd/pull/14199
+// Usually it's a no-op, but the attribute can apply to the function/delegate type of the variable
+// The current behavior is weird, so this is a test of the current behavior, not necessarily the desired behavior
+
+// No-op
+pure int x;
+
+// Applies to function type (existing code in dmd and Phobos relies on this)
+pure void function() pf = () {
+    static int g;
+    g++;
+};
+
+// Function attributes currently don't apply to inferred types (somewhat surprisingly)
+nothrow nf = () {
+       throw new Exception("");
+};
+
+// Neither do they apply to indirections
+alias F = void function();
+
+pure F pf2 = () {
+    static int g;
+    g++;
+};
index dc285a5dfab10fa3fe99f3030188ead0c56a4a3d..2b469d2f8038e0aae2f927e3f109c7faf03056c9 100644 (file)
@@ -24,7 +24,7 @@ class Abc : Throwable
     {
         printf("foo 1\n");
         x |= 4;
-        throw this;
+        throw cast() this;
         printf("foo 2\n");
         x |= 8;
     }
index 5987cb4be7bdd2bccdc0f1bf097334adff2c1712..02301b9d1d2267af3434c43f294eea970dbeb61c 100644 (file)
@@ -1,7 +1,7 @@
 module imports.link11069z;
 struct Matrix(T, uint _M)
 {
-    int opCmp()(auto ref in Matrix b) const
+    int opCmp()(const auto ref Matrix b) const
     {
         return 0;
     }
index de3bbe2ac9bbf06e37c9c7cd02bbbde3db3cd841..077671b01c41cf9637e31ecbd24db7eb701e235b 100644 (file)
@@ -7,7 +7,7 @@ struct S(alias func)
     }
 }
 
-extern(C) int printf(in char*, ...);
+extern(C) int printf(const char*, ...);
 
 void f(int i = 77)
 {
diff --git a/gcc/testsuite/gdc.test/runnable/imports/mainx23837.c b/gcc/testsuite/gdc.test/runnable/imports/mainx23837.c
new file mode 100644 (file)
index 0000000..0c446ab
--- /dev/null
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug?id=23837
+
+struct stbrp_context
+{
+       int width;
+       int height;
+       int align;
+       int init_mode;
+       int heuristic;
+};
index 7599e0e03c389cd47e377e51eace9837a586e927..6e8f2b2898738d05dba7e04f553f8ccf16ff902b 100644 (file)
@@ -571,12 +571,6 @@ void test12231()
 
 /***************************************************/
 
-int test2a(scope int a) { return a; }
-
-static assert(test2a.mangleof == "_D6mangle6test2aFiZi");
-
-/***************************************************/
-
 class CC
 {
     int* p;
diff --git a/gcc/testsuite/gdc.test/runnable/test23837.d b/gcc/testsuite/gdc.test/runnable/test23837.d
new file mode 100644 (file)
index 0000000..4e155b6
--- /dev/null
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=23837
+// EXTRA_FILES: imports/mainx23837.c
+
+import imports.mainx23837;
+
+struct TexturePacker
+{
+       stbrp_context _context;
+}
+
+int main()
+{
+    auto res = TexturePacker();
+    return 0;
+}
index 57045ba00e70dce13908f36a57d96045bac56dfa..436f707b62069b877834cab826c86097a48da7f7 100644 (file)
@@ -2113,7 +2113,7 @@ void test12725()
 
 struct Matrix12728(T, uint m, uint n = m, ubyte f = 0)
 {
-    void foo(uint r)(auto ref in Matrix12728!(T, n, r) b)
+    void foo(uint r)(const auto ref Matrix12728!(T, n, r) b)
     {
     }
 }
index e57f52f6578364f9a3b19fdea5b8e5a17289d3b7..5017a767693f1cdc1cf0cde62c60b1a29b241be0 100644 (file)
@@ -5016,18 +5016,18 @@ void test6763()
 {
     int n;
 
-    f6763(0);   //With D2: Error: function main.f ((ref const const(int) _param_0)) is not callable using argument types (int)
+    f6763(0);   //With D2: Error: function main.f ((ref const const(int) __param_0)) is not callable using argument types (int)
     c6763(0);
     r6763(n);   static assert(__traits(compiles, r6763(0)));
     i6763(0);
     o6763(n);   static assert(!__traits(compiles, o6763(0)));
 
     // https://issues.dlang.org/show_bug.cgi?id=6755
-    static assert(typeof(f6763).stringof == "void(int _param_0)");
-    static assert(typeof(c6763).stringof == "void(const(int) _param_0)");
-    static assert(typeof(r6763).stringof == "void(ref int _param_0)");
-    static assert(typeof(i6763).stringof == "void(in int _param_0)");
-    static assert(typeof(o6763).stringof == "void(out int _param_0)");
+    static assert(typeof(f6763).stringof == "void(int __param_0)");
+    static assert(typeof(c6763).stringof == "void(const(int) __param_0)");
+    static assert(typeof(r6763).stringof == "void(ref int __param_0)");
+    static assert(typeof(i6763).stringof == "void(in int __param_0)");
+    static assert(typeof(o6763).stringof == "void(out int __param_0)");
 }
 
 /***************************************************/
@@ -5997,7 +5997,7 @@ void test7618(const int x = 1)
 {
     int func(ref int x) { return 1; }
     static assert(!__traits(compiles, func(x)));
-    // Error: function test.foo.func (ref int _param_0) is not callable using argument types (const(int))
+    // Error: function test.foo.func (ref int __param_0) is not callable using argument types (const(int))
 
     int delegate(ref int) dg = (ref int x) => 1;
     static assert(!__traits(compiles, dg(x)));
@@ -6170,14 +6170,6 @@ static assert(!__traits(compiles, foo8220(typeof(0)))); // fail
 
 /***************************************************/
 
-void func8105(in ref int x) { }
-
-void test8105()
-{
-}
-
-/***************************************************/
-
 template ParameterTypeTuple159(alias foo)
 {
     static if (is(typeof(foo) P == __parameters))
@@ -8300,7 +8292,6 @@ int main()
     test12503();
     test8004();
     test8064();
-    test8105();
     test159();
     test12824();
     test8283();
index 1205cd941b79971fbfc314401c91cc16b43d9abb..95ea67d789bd245c92b0cf0d12a4212fa1e926a6 100644 (file)
@@ -1,4 +1,4 @@
-a45f4e9f43e9fdbf0b666175e5e66b1ce4f561f6
+28a3b24c2e45de39cd3df528142fd06b6456e8fd
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
index 4af3fdf2bd294cd441e0068d0911336c3cfdd0c6..1fba06cc0214279bcbc7ef5049e3e8d7ee4c6b48 100644 (file)
@@ -2,6 +2,9 @@
  * The atomic module provides basic support for lock-free
  * concurrent programming.
  *
+ * $(NOTE Use the `-preview=nosharedaccess` compiler flag to detect
+ * unsafe individual read or write operations on shared data.)
+ *
  * Copyright: Copyright Sean Kelly 2005 - 2016.
  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
  * Authors:   Sean Kelly, Alex Rønne Petersen, Manu Evans
 
 module core.atomic;
 
+///
+@safe unittest
+{
+    int y = 2;
+    shared int x = y; // OK
+
+    //x++; // read modify write error
+    x.atomicOp!"+="(1); // OK
+    //y = x; // read error with preview flag
+    y = x.atomicLoad(); // OK
+    assert(y == 3);
+    //x = 5; // write error with preview flag
+    x.atomicStore(5); // OK
+    assert(x.atomicLoad() == 5);
+}
+
 import core.internal.atomic;
 import core.internal.attributes : betterC;
 import core.internal.traits : hasUnsharedIndirections;
@@ -1089,42 +1108,6 @@ version (CoreUnittest)
         assert(ptr is null);
     }
 
-    unittest
-    {
-        import core.thread;
-
-        // Use heap memory to ensure an optimizing
-        // compiler doesn't put things in registers.
-        uint* x = new uint();
-        bool* f = new bool();
-        uint* r = new uint();
-
-        auto thr = new Thread(()
-        {
-            while (!*f)
-            {
-            }
-
-            atomicFence();
-
-            *r = *x;
-        });
-
-        thr.start();
-
-        *x = 42;
-
-        atomicFence();
-
-        *f = true;
-
-        atomicFence();
-
-        thr.join();
-
-        assert(*r == 42);
-    }
-
     // === atomicFetchAdd and atomicFetchSub operations ====
     @betterC pure nothrow @nogc @safe unittest
     {
index 3fcb26657c84074e4a446edac1ebed45a4ae176a..f08e1f864441d6a236df8d3ba9e84e291329f39b 100644 (file)
@@ -69,62 +69,17 @@ pure @safe:
     {
         buf     = buf_;
         addType = addType_;
-        dst     = dst_;
+        dst.dst = dst_;
     }
 
-
-    enum size_t minBufSize = 4000;
-
-
     const(char)[]   buf     = null;
-    char[]          dst     = null;
+    Buffer          dst;
     size_t          pos     = 0;
-    size_t          len     = 0;
     size_t          brp     = 0; // current back reference pos
     AddType         addType = AddType.yes;
     bool            mute    = false;
     Hooks           hooks;
 
-    static class ParseException : Exception
-    {
-        this(string msg) @safe pure nothrow
-        {
-            super( msg );
-        }
-    }
-
-
-    static class OverflowException : Exception
-    {
-        this(string msg) @safe pure nothrow
-        {
-            super( msg );
-        }
-    }
-
-
-    static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
-    {
-        pragma(inline, false); // tame dmd inliner
-
-        //throw new ParseException( msg );
-        debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
-        throw __ctfe ? new ParseException(msg)
-                     : cast(ParseException) __traits(initSymbol, ParseException).ptr;
-
-    }
-
-
-    static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
-    {
-        pragma(inline, false); // tame dmd inliner
-
-        //throw new OverflowException( msg );
-        debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
-        throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr;
-    }
-
-
     //////////////////////////////////////////////////////////////////////////
     // Type Testing and Conversion
     //////////////////////////////////////////////////////////////////////////
@@ -163,91 +118,11 @@ pure @safe:
         error();
     }
 
-
-    //////////////////////////////////////////////////////////////////////////
-    // Data Output
-    //////////////////////////////////////////////////////////////////////////
-
-
-    static bool contains( const(char)[] a, const(char)[] b ) @trusted
-    {
-        if (a.length && b.length)
-        {
-            auto bend = b.ptr + b.length;
-            auto aend = a.ptr + a.length;
-            return a.ptr <= b.ptr && bend <= aend;
-        }
-        return false;
-    }
-
-
-    // move val to the end of the dst buffer
-    char[] shift( const(char)[] val )
-    {
-        pragma(inline, false); // tame dmd inliner
-
-        if ( val.length && !mute )
-        {
-            assert( contains( dst[0 .. len], val ) );
-            debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
-
-            if (len + val.length > dst.length)
-                overflow();
-            size_t v = &val[0] - &dst[0];
-            dst[len .. len + val.length] = val[];
-            for (size_t p = v; p < len; p++)
-                dst[p] = dst[p + val.length];
-
-            return dst[len - val.length .. len];
-        }
-        return null;
-    }
-
-    // remove val from dst buffer
-    void remove( const(char)[] val )
+    char[] shift(scope const(char)[] val) return scope
     {
-        pragma(inline, false); // tame dmd inliner
-
-        if ( val.length )
-        {
-            assert( contains( dst[0 .. len], val ) );
-            debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
-            size_t v = &val[0] - &dst[0];
-            assert( len >= val.length && len <= dst.length );
-            len -= val.length;
-            for (size_t p = v; p < len; p++)
-                dst[p] = dst[p + val.length];
-        }
-    }
-
-    char[] append( const(char)[] val ) return scope
-    {
-        pragma(inline, false); // tame dmd inliner
-
-        if ( val.length && !mute )
-        {
-            if ( !dst.length )
-                dst.length = minBufSize;
-            assert( !contains( dst[0 .. len], val ) );
-            debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
-
-            if ( dst.length - len >= val.length && &dst[len] == &val[0] )
-            {
-                // data is already in place
-                auto t = dst[len .. len + val.length];
-                len += val.length;
-                return t;
-            }
-            if ( dst.length - len >= val.length )
-            {
-                dst[len .. len + val.length] = val[];
-                auto t = dst[len .. len + val.length];
-                len += val.length;
-                return t;
-            }
-            overflow();
-        }
-        return null;
+        if (mute)
+            return null;
+        return dst.shift(val);
     }
 
     void putComma(size_t n)
@@ -265,14 +140,9 @@ pure @safe:
 
     void put(scope const(char)[] val) return scope
     {
-        pragma(inline, false); // tame dmd inliner
-
-        if (!val.length) return;
-
-        if (!contains(dst[0 .. len], val))
-            append(val);
-        else
-            shift(val);
+        if (mute)
+            return;
+        dst.append(val);
     }
 
 
@@ -297,7 +167,7 @@ pure @safe:
     {
         if ( val.length )
         {
-            append( " " );
+            put(" ");
             put( val );
         }
     }
@@ -307,7 +177,7 @@ pure @safe:
     {
         debug(trace) printf( "silent+\n" );
         debug(trace) scope(success) printf( "silent-\n" );
-        auto n = len; dg(); len = n;
+        auto n = dst.length; dg(); dst.len = n;
     }
 
 
@@ -835,7 +705,7 @@ pure @safe:
 
         debug(trace) printf( "parseType+\n" );
         debug(trace) scope(success) printf( "parseType-\n" );
-        auto beg = len;
+        auto beg = dst.length;
         auto t = front;
 
         char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe
@@ -867,19 +737,19 @@ pure @safe:
             put( "shared(" );
             parseType();
             put( ')' );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'x': // Const (x Type)
             popFront();
             put( "const(" );
             parseType();
             put( ')' );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'y': // Immutable (y Type)
             popFront();
             put( "immutable(" );
             parseType();
             put( ')' );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'N':
             popFront();
             switch ( front )
@@ -887,29 +757,28 @@ pure @safe:
             case 'n': // Noreturn
                 popFront();
                 put("noreturn");
-                return dst[beg .. len];
+                return dst[beg .. $];
             case 'g': // Wild (Ng Type)
                 popFront();
                 // TODO: Anything needed here?
                 put( "inout(" );
                 parseType();
                 put( ')' );
-                return dst[beg .. len];
+                return dst[beg .. $];
             case 'h': // TypeVector (Nh Type)
                 popFront();
                 put( "__vector(" );
                 parseType();
                 put( ')' );
-                return dst[beg .. len];
+                return dst[beg .. $];
             default:
                 error();
-                assert( 0 );
             }
         case 'A': // TypeArray (A Type)
             popFront();
             parseType();
             put( "[]" );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'G': // TypeStaticArray (G Number Type)
             popFront();
             auto num = sliceNumber();
@@ -917,21 +786,21 @@ pure @safe:
             put( '[' );
             put( num );
             put( ']' );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'H': // TypeAssocArray (H Type Type)
             popFront();
             // skip t1
             auto tx = parseType();
             parseType();
             put( '[' );
-            put( tx );
+            shift(tx);
             put( ']' );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'P': // TypePointer (P Type)
             popFront();
             parseType();
             put( '*' );
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
             return parseTypeFunction();
         case 'C': // TypeClass (C LName)
@@ -940,7 +809,7 @@ pure @safe:
         case 'T': // TypeTypedef (T LName)
             popFront();
             parseQualifiedName();
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'D': // TypeDelegate (D TypeFunction)
             popFront();
             auto modifiers = parseModifier();
@@ -957,15 +826,15 @@ pure @safe:
                     put(str);
                 }
             }
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'n': // TypeNone (n)
             popFront();
             // TODO: Anything needed here?
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'B': // TypeTuple (B Number Arguments)
             popFront();
             // TODO: Handle this.
-            return dst[beg .. len];
+            return dst[beg .. $];
         case 'Z': // Internal symbol
             // This 'type' is used for untyped internal symbols, i.e.:
             // __array
@@ -975,13 +844,13 @@ pure @safe:
             // __Interface
             // __ModuleInfo
             popFront();
-            return dst[beg .. len];
+            return dst[beg .. $];
         default:
             if (t >= 'a' && t <= 'w')
             {
                 popFront();
                 put( primitives[cast(size_t)(t - 'a')] );
-                return dst[beg .. len];
+                return dst[beg .. $];
             }
             else if (t == 'z')
             {
@@ -991,14 +860,13 @@ pure @safe:
                 case 'i':
                     popFront();
                     put( "cent" );
-                    return dst[beg .. len];
+                    return dst[beg .. $];
                 case 'k':
                     popFront();
                     put( "ucent" );
-                    return dst[beg .. len];
+                    return dst[beg .. $];
                 default:
                     error();
-                    assert( 0 );
                 }
             }
             error();
@@ -1351,12 +1219,13 @@ pure @safe:
     {
         debug(trace) printf( "parseTypeFunction+\n" );
         debug(trace) scope(success) printf( "parseTypeFunction-\n" );
-        auto beg = len;
+        auto beg = dst.length;
 
         parseCallConvention();
         auto attributes = parseFuncAttr();
 
-        auto argbeg = len;
+        auto argbeg = dst.length;
+        put(IsDelegate.yes == isdg ? "delegate" : "function");
         put( '(' );
         parseFuncArguments();
         put( ')' );
@@ -1369,17 +1238,18 @@ pure @safe:
                 put(str);
             }
         }
-        auto retbeg = len;
-        parseType();
-        put( ' ' );
-        // append delegate/function
-        if (IsDelegate.yes == isdg)
-            put( "delegate" );
-        else
-            put( "function" );
-        // move arguments and attributes behind name
-        shift( dst[argbeg .. retbeg] );
-        return dst[beg..len];
+
+        // A function / delegate return type is located at the end of its mangling
+        // Write it in order, then shift it back to 'code order'
+        // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe'
+        {
+            auto retbeg = dst.length;
+            parseType();
+            put(' ');
+            shift(dst[argbeg .. retbeg]);
+        }
+
+        return dst[beg .. $];
     }
 
     static bool isCallConvention( char ch )
@@ -1701,7 +1571,7 @@ pure @safe:
 
                 if ( mayBeMangledNameArg() )
                 {
-                    auto l = len;
+                    auto l = dst.length;
                     auto p = pos;
                     auto b = brp;
                     try
@@ -1712,7 +1582,7 @@ pure @safe:
                     }
                     catch ( ParseException e )
                     {
-                        len = l;
+                        dst.len = l;
                         pos = p;
                         brp = b;
                         debug(trace) printf( "not a mangled name arg\n" );
@@ -1724,7 +1594,7 @@ pure @safe:
                     // try all possible pairs of numbers
                     auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName
                     pos--;
-                    auto l = len;
+                    auto l = dst.length;
                     auto p = pos;
                     auto b = brp;
                     while ( qlen > 0 )
@@ -1740,7 +1610,7 @@ pure @safe:
                         }
                         qlen /= 10; // retry with one digit less
                         pos = --p;
-                        len = l;
+                        dst.len = l;
                         brp = b;
                     }
                 }
@@ -1860,7 +1730,7 @@ pure @safe:
         case '0': .. case '9':
             if ( mayBeTemplateInstanceName() )
             {
-                auto t = len;
+                auto t = dst.length;
 
                 try
                 {
@@ -1871,7 +1741,7 @@ pure @safe:
                 catch ( ParseException e )
                 {
                     debug(trace) printf( "not a template instance name\n" );
-                    len = t;
+                    dst.len = t;
                 }
             }
             goto case;
@@ -1889,10 +1759,9 @@ pure @safe:
     {
         // try to demangle a function, in case we are pointing to some function local
         auto prevpos = pos;
-        auto prevlen = len;
+        auto prevlen = dst.length;
         auto prevbrp = brp;
 
-        char[] attr;
         try
         {
             if ( 'M' == front )
@@ -1908,6 +1777,7 @@ pure @safe:
             }
             if ( isCallConvention( front ) )
             {
+                char[] attr;
                 // we don't want calling convention and attributes in the qualified name
                 parseCallConvention();
                 auto attributes = parseFuncAttr();
@@ -1917,23 +1787,23 @@ pure @safe:
                         put(str);
                         put(' ');
                     }
-                    attr = dst[prevlen .. len];
+                    attr = dst[prevlen .. $];
                 }
 
                 put( '(' );
                 parseFuncArguments();
                 put( ')' );
+                return attr;
             }
         }
         catch ( ParseException )
         {
             // not part of a qualified name, so back up
             pos = prevpos;
-            len = prevlen;
+            dst.len = prevlen;
             brp = prevbrp;
-            attr = null;
         }
-        return attr;
+        return null;
     }
 
     /*
@@ -1945,7 +1815,7 @@ pure @safe:
     {
         debug(trace) printf( "parseQualifiedName+\n" );
         debug(trace) scope(success) printf( "parseQualifiedName-\n" );
-        size_t  beg = len;
+        size_t  beg = dst.length;
         size_t  n   = 0;
 
         do
@@ -1956,7 +1826,7 @@ pure @safe:
             parseFunctionTypeNoReturn();
 
         } while ( isSymbolNameFront() );
-        return dst[beg .. len];
+        return dst[beg .. $];
     }
 
 
@@ -1977,17 +1847,17 @@ pure @safe:
         match( 'D' );
         do
         {
-            size_t  beg = len;
-            size_t  nameEnd = len;
+            size_t  beg = dst.length;
+            size_t  nameEnd = dst.length;
             char[] attr;
             do
             {
                 if ( attr )
-                    remove( attr ); // dump attributes of parent symbols
-                if ( beg != len )
+                    dst.remove(attr); // dump attributes of parent symbols
+                if (beg != dst.length)
                     put( '.' );
                 parseSymbolName();
-                nameEnd = len;
+                nameEnd = dst.length;
                 attr = parseFunctionTypeNoReturn( displayType );
 
             } while ( isSymbolNameFront() );
@@ -1995,7 +1865,7 @@ pure @safe:
             if ( displayType )
             {
                 attr = shift( attr );
-                nameEnd = len - attr.length;  // name includes function arguments
+                nameEnd = dst.length - attr.length;  // name includes function arguments
             }
             name = dst[beg .. nameEnd];
 
@@ -2003,7 +1873,7 @@ pure @safe:
             if ( 'M' == front )
                 popFront(); // has 'this' pointer
 
-            auto lastlen = len;
+            auto lastlen = dst.length;
             auto type = parseType();
             if ( displayType )
             {
@@ -2016,7 +1886,7 @@ pure @safe:
             {
                 // remove type
                 assert( attr.length == 0 );
-                len = lastlen;
+                dst.len = lastlen;
             }
             if ( pos >= buf.length || (n != 0 && pos >= end) )
                 return;
@@ -2040,15 +1910,6 @@ pure @safe:
         parseMangledName( AddType.yes == addType );
     }
 
-    char[] copyInput() return scope
-    {
-        if (dst.length < buf.length)
-            dst.length = buf.length;
-        char[] r = dst[0 .. buf.length];
-        r[] = buf[];
-        return r;
-    }
-
     char[] doDemangle(alias FUNC)() return scope
     {
         while ( true )
@@ -2057,17 +1918,17 @@ pure @safe:
             {
                 debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr );
                 FUNC();
-                return dst[0 .. len];
+                return dst[0 .. $];
             }
             catch ( OverflowException e )
             {
                 debug(trace) printf( "overflow... restarting\n" );
-                auto a = minBufSize;
-                auto b = 2 * dst.length;
+                auto a = Buffer.minSize;
+                auto b = 2 * dst.dst.length;
                 auto newsz = a < b ? b : a;
                 debug(info) printf( "growing dst to %lu bytes\n", newsz );
-                dst.length = newsz;
-                pos = len = brp = 0;
+                dst.dst.length = newsz;
+                pos = dst.len = brp = 0;
                 continue;
             }
             catch ( ParseException e )
@@ -2077,7 +1938,7 @@ pure @safe:
                     auto msg = e.toString();
                     printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
                 }
-                return copyInput();
+                return dst.copyInput(buf);
             }
             catch ( Exception e )
             {
@@ -2119,7 +1980,7 @@ char[] demangle(return scope const(char)[] buf, return scope char[] dst = null,
     // fast path (avoiding throwing & catching exception) for obvious
     // non-D mangled names
     if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
-        return d.copyInput();
+        return d.dst.copyInput(buf);
     return d.demangleName();
 }
 
@@ -2212,7 +2073,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
                     d.popFront();
                     size_t n = d.decodeBackref();
                     if (!n || n > refpos)
-                        d.error("invalid back reference");
+                        error("invalid back reference");
 
                     auto savepos = d.pos;
                     scope(exit) d.pos = savepos;
@@ -2220,11 +2081,11 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
 
                     auto idlen = d.decodeNumber();
                     if (d.pos + idlen > d.buf.length)
-                        d.error("invalid back reference");
+                        error("invalid back reference");
                     auto id = d.buf[d.pos .. d.pos + idlen];
                     auto pid = id in idpos;
                     if (!pid)
-                        d.error("invalid back reference");
+                        error("invalid back reference");
                     npos = positionInResult(*pid);
                 }
                 encodeBackref(reslen - npos);
@@ -2235,7 +2096,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
             {
                 auto n = d.decodeNumber();
                 if (!n || n > d.buf.length || n > d.buf.length - d.pos)
-                    d.error("LName too shot or too long");
+                    error("LName too shot or too long");
                 auto id = d.buf[d.pos .. d.pos + n];
                 d.pos += n;
                 if (auto pid = id in idpos)
@@ -2267,7 +2128,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
             d.popFront();
             auto n = d.decodeBackref();
             if (n == 0 || n > refPos)
-                d.error("invalid back reference");
+                error("invalid back reference");
 
             size_t npos = positionInResult(refPos - n);
             size_t reslen = result.length;
@@ -2903,6 +2764,7 @@ private shared CXX_DEMANGLER __cxa_demangle;
 
 CXX_DEMANGLER getCXXDemangler() nothrow @trusted
 {
+    import core.atomic : atomicLoad, atomicStore;
     if (__cxa_demangle is null)
     version (Posix)
     {
@@ -2916,17 +2778,21 @@ CXX_DEMANGLER getCXXDemangler() nothrow @trusted
         version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT;
 
         if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle"))
-            __cxa_demangle = found;
+            atomicStore(__cxa_demangle, found);
     }
 
     if (__cxa_demangle is null)
-        __cxa_demangle = (const char* mangled_name, char* output_buffer,
-                            size_t* length, int* status) nothrow pure @trusted {
-                                *status = -1;
-                                return null;
-                            };
+    {
+        static extern(C) char* _(const char* mangled_name, char* output_buffer,
+             size_t* length, int* status) nothrow pure @trusted
+        {
+            *status = -1;
+            return null;
+        }
+        atomicStore(__cxa_demangle, &_);
+    }
 
-    return __cxa_demangle;
+    return atomicLoad(__cxa_demangle);
 }
 
 /**
@@ -2967,3 +2833,163 @@ private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_d
     dst[] = buf[];
     return dst;
 }
+
+/**
+ * Error handling through Exceptions
+ *
+ * The following types / functions are only used in this module,
+ * hence why the functions are `@trusted`.
+ * To make things `@nogc`, default-initialized instances are thrown.
+ */
+private class ParseException : Exception
+{
+    public this(string msg) @safe pure nothrow
+    {
+        super(msg);
+    }
+}
+
+/// Ditto
+private class OverflowException : Exception
+{
+    public this(string msg) @safe pure nothrow
+    {
+        super(msg);
+    }
+}
+
+/// Ditto
+private noreturn error(string msg = "Invalid symbol") @trusted pure
+{
+    pragma(inline, false); // tame dmd inliner
+
+    //throw new ParseException( msg );
+    debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
+    throw __ctfe ? new ParseException(msg)
+        : cast(ParseException) __traits(initSymbol, ParseException).ptr;
+}
+
+/// Ditto
+private noreturn overflow(string msg = "Buffer overflow") @trusted pure
+{
+    pragma(inline, false); // tame dmd inliner
+
+    //throw new OverflowException( msg );
+    debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
+    throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr;
+}
+
+private struct Buffer
+{
+    enum size_t minSize = 4000;
+
+    @safe pure:
+
+    private char[] dst;
+    private size_t len;
+
+    public alias opDollar = len;
+
+    public size_t length () const scope @safe pure nothrow @nogc
+    {
+        return this.len;
+    }
+
+    public inout(char)[] opSlice (size_t from, size_t to)
+        inout return scope @safe pure nothrow @nogc
+    {
+        assert(from <= to);
+        assert(to <= len);
+        return this.dst[from .. to];
+    }
+
+    static bool contains(scope const(char)[] a, scope const(char)[] b) @trusted
+    {
+        if (a.length && b.length)
+        {
+            auto bend = b.ptr + b.length;
+            auto aend = a.ptr + a.length;
+            return a.ptr <= b.ptr && bend <= aend;
+        }
+        return false;
+    }
+
+    char[] copyInput(scope const(char)[] buf)
+        return scope nothrow
+    {
+        if (dst.length < buf.length)
+            dst.length = buf.length;
+        char[] r = dst[0 .. buf.length];
+        r[] = buf[];
+        return r;
+    }
+
+    // move val to the end of the dst buffer
+    char[] shift(scope const(char)[] val) return scope
+    {
+        pragma(inline, false); // tame dmd inliner
+
+        if (val.length)
+        {
+            assert( contains( dst[0 .. len], val ) );
+            debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
+
+            if (len + val.length > dst.length)
+                overflow();
+            size_t v = &val[0] - &dst[0];
+            dst[len .. len + val.length] = val[];
+            for (size_t p = v; p < len; p++)
+                dst[p] = dst[p + val.length];
+
+            return dst[len - val.length .. len];
+        }
+        return null;
+    }
+
+    // remove val from dst buffer
+    void remove(scope const(char)[] val) scope
+    {
+        pragma(inline, false); // tame dmd inliner
+
+        if ( val.length )
+        {
+            assert( contains( dst[0 .. len], val ) );
+            debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
+            size_t v = &val[0] - &dst[0];
+            assert( len >= val.length && len <= dst.length );
+            len -= val.length;
+            for (size_t p = v; p < len; p++)
+                dst[p] = dst[p + val.length];
+        }
+    }
+
+    char[] append(scope const(char)[] val) return scope
+    {
+        pragma(inline, false); // tame dmd inliner
+
+        if (val.length)
+        {
+            if ( !dst.length )
+                dst.length = minSize;
+            assert( !contains( dst[0 .. len], val ) );
+            debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
+
+            if ( dst.length - len >= val.length && &dst[len] == &val[0] )
+            {
+                // data is already in place
+                auto t = dst[len .. len + val.length];
+                len += val.length;
+                return t;
+            }
+            if ( dst.length - len >= val.length )
+            {
+                dst[len .. len + val.length] = val[];
+                auto t = dst[len .. len + val.length];
+                len += val.length;
+                return t;
+            }
+            overflow();
+        }
+        return null;
+    }
+}
index 99f33da7683dc2abab463ac5e9960e10f262a430..ff777a6b3ab68ca0e1ae255592b215395039bd2d 100644 (file)
 */
 module core.internal.array.concatenation;
 
-/// See $(REF _d_arraycatnTX, rt,lifetime)
-private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow;
-
-/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace`
-template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T)
+/**
+ * Concatenate the arrays inside of `froms`.
+ * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`.
+ *
+ * Params:
+ *      froms = Arrays to be concatenated.
+ * Returns:
+ *      A newly allocated array that contains all the elements from `froms`.
+ */
+Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted
 {
-    private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!";
+    import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+    import core.lifetime : copyEmplace;
+    import core.stdc.string : memcpy;
 
-    /**
-    * Concatenating the arrays inside of `arrs`.
-    * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`.
-    * Params:
-    *  arrs = Array containing arrays that will be concatenated.
-    * Returns:
-    *  A newly allocated array that contains all the elements from all the arrays in `arrs`.
-    * Bugs:
-    *  This function template was ported from a much older runtime hook that bypassed safety,
-    *  purity, and throwabilty checks. To prevent breaking existing code, this function template
-    *  is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
-    */
-    ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow
+    Tret res;
+    size_t totalLen;
+
+    alias T = typeof(res[0]);
+    enum elemSize = T.sizeof;
+    enum hasPostblit = __traits(hasPostblit, T);
+
+    static foreach (from; froms)
+        static if (is (typeof(from) : T))
+            totalLen++;
+        else
+            totalLen += from.length;
+
+    if (totalLen == 0)
+        return res;
+    res.length = totalLen;
+
+    /* Currently, if both a postblit and a cpctor are defined, the postblit is
+     * used. If this changes, the condition below will have to be adapted.
+     */
+    static if (hasElaborateCopyConstructor!T && !hasPostblit)
     {
-        pragma(inline, false);
-        version (D_TypeInfo)
-        {
-            auto ti = typeid(ResultArrT);
+        size_t i = 0;
+        foreach (ref from; froms)
+            static if (is (typeof(from) : T))
+                copyEmplace(cast(T) from, res[i++]);
+            else
+            {
+                if (from.length)
+                    foreach (ref elem; from)
+                        copyEmplace(cast(T) elem, res[i++]);
+            }
+    }
+    else
+    {
+        auto resptr = cast(Unqual!T *) res;
+        foreach (ref from; froms)
+            static if (is (typeof(from) : T))
+                memcpy(resptr++, cast(Unqual!T *) &from, elemSize);
+            else
+            {
+                const len = from.length;
+                if (len)
+                {
+                    memcpy(resptr, cast(Unqual!T *) from, len * elemSize);
+                    resptr += len;
+                }
+            }
+
+        static if (hasPostblit)
+            foreach (ref elem; res)
+                (cast() elem).__xpostblit();
+    }
+
+    return res;
+}
 
-            byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length];
-            void[] result = ._d_arraycatnTX(ti, arrs2);
-            return (cast(T*)result.ptr)[0 .. result.length];
+// postblit
+@safe unittest
+{
+    int counter;
+    struct S
+    {
+        int val;
+        this(this)
+        {
+            counter++;
         }
-        else
-            assert(0, errorMessage);
     }
 
-    version (D_ProfileGC)
+    S[] arr1 = [S(0), S(1), S(2)];
+    S[] arr2 = [];
+    S[] arr3 = [S(6), S(7), S(8)];
+    S elem = S(9);
+    S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem);
+
+    assert(counter == 7);
+    assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]);
+}
+
+// copy constructor
+@safe unittest
+{
+    int counter;
+    struct S
     {
-        import core.internal.array.utils : _d_HookTraceImpl;
-
-        /**
-         * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat).
-         * Bugs:
-         *  This function template was ported from a much older runtime hook that bypassed safety,
-         *  purity, and throwabilty checks. To prevent breaking existing code, this function template
-         *  is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
-         */
-        alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage);
+        int val;
+        this(ref return scope S rhs)
+        {
+            val = rhs.val;
+            counter++;
+        }
     }
+
+    S[] arr1 = [S(0), S(1), S(2)];
+    S[] arr2 = [S(3), S(4), S(5)];
+    S[] arr3 = [S(6), S(7), S(8)];
+    S elem = S(9);
+    S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3);
+
+    assert(counter == 10);
+    assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]);
 }
 
+// throwing
 @safe unittest
 {
     int counter;
+    bool didThrow;
     struct S
     {
         int val;
         this(this)
         {
             counter++;
+            if (counter == 4)
+                throw new Exception("");
         }
     }
 
-    S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]];
-    S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr);
+    try
+    {
+        S[] arr1 = [S(0), S(1), S(2)];
+        S[] arr2 = [S(3), S(4), S(5)];
+        _d_arraycatnTX!(S[])(arr1, arr2);
+    }
+    catch (Exception)
+    {
+        didThrow = true;
+    }
+
+    assert(counter == 4);
+    assert(didThrow);
+}
+
+version (D_ProfileGC)
+{
+    /**
+    * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation).
+    */
+    Tret _d_arraycatnTXTrace(Tret, Tarr...)(string file, int line, string funcname, scope auto ref Tarr froms) @trusted
+    {
+        version (D_TypeInfo)
+        {
+            import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
+            mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX"));
 
-    assert(counter == 8);
-    assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]);
+            import core.lifetime: forward;
+            return _d_arraycatnTX!Tret(forward!froms);
+        }
+        else
+            assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!");
+    }
 }
index 6d19247fbe01ec9033f796eac95b88a23850bced..62ce941e39389f0a04401bb5edb3280252ef2bc9 100644 (file)
@@ -1509,7 +1509,7 @@ struct Gcx
     List*[Bins.B_NUMSMALL] bucket; // free list for each small size
 
     // run a collection when reaching those thresholds (number of used pages)
-    float smallCollectThreshold, largeCollectThreshold;
+    float smallCollectThreshold = 0.0f, largeCollectThreshold = 0.0f;
     uint usedSmallPages, usedLargePages;
     // total number of mapped pages
     uint mappedPages;
@@ -3529,7 +3529,7 @@ struct Pool
         Small = 4,
         Large = 12
     }
-    ShiftBy shiftBy;    // shift count for the divisor used for determining bit indices.
+    ShiftBy shiftBy = void;    // shift count for the divisor used for determining bit indices.
 
     // This tracks how far back we have to go to find the nearest B_PAGE at
     // a smaller address than a B_PAGEPLUS.  To save space, we use a uint.
index 64a9cc92ffba8b70776bc8c5fb19dcda6be0a96c..e09bba4707b5223e29f43a918f4a54cae3704f23 100644 (file)
@@ -12,26 +12,57 @@ module core.internal.string;
 pure:
 nothrow:
 @nogc:
+@safe:
 
-alias UnsignedStringBuf = char[20];
+alias UnsignedStringBuf = char[64];
 
 /**
 Converts an unsigned integer value to a string of characters.
 
-This implementation is a template so it can be used when compiling with -betterC.
+Can be used when compiling with -betterC. Does not allocate memory.
 
 Params:
+    T = char, wchar or dchar
     value = the unsigned integer value to convert
     buf   = the pre-allocated buffer used to store the result
-    radix = the numeric base to use in the conversion (defaults to 10)
+    radix = the numeric base to use in the conversion 2 through 36 (defaults to 10)
+    upperCase = use upper case letters for radices 11 - 36
 
 Returns:
     The unsigned integer value as a string of characters
 */
-char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe
-if (radix >= 2 && radix <= 16)
+T[] unsignedToTempString(uint radix = 10, bool upperCase = false, T)(ulong value, return scope T[] buf)
+if (radix >= 2 && radix <= 36 &&
+    (is(T == char) || is(T == wchar) || is(T == dchar)))
 {
+    enum baseChar = upperCase ? 'A' : 'a';
     size_t i = buf.length;
+
+    static if (size_t.sizeof == 4) // 32 bit CPU
+    {
+        if (value <= uint.max)
+        {
+            // use faster 32 bit arithmetic
+            uint val = cast(uint) value;
+            do
+            {
+                uint x = void;
+                if (val < radix)
+                {
+                    x = cast(uint)val;
+                    val = 0;
+                }
+                else
+                {
+                    x = cast(uint)(val % radix);
+                    val /= radix;
+                }
+                buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar);
+            } while (val);
+            return buf[i .. $];
+        }
+    }
+
     do
     {
         uint x = void;
@@ -45,7 +76,7 @@ if (radix >= 2 && radix <= 16)
             x = cast(uint)(value % radix);
             value /= radix;
         }
-        buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a');
+        buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar);
     } while (value);
     return buf[i .. $];
 }
@@ -73,7 +104,7 @@ Params:
 Returns:
     The unsigned integer value as a string of characters
 */
-auto unsignedToTempString(uint radix = 10)(ulong value) @safe
+auto unsignedToTempString(uint radix = 10)(ulong value)
 {
     // Need a buffer of 65 bytes for radix of 2 with room for
     // signedToTempString to possibly add a negative sign.
@@ -85,11 +116,12 @@ auto unsignedToTempString(uint radix = 10)(ulong value) @safe
 
 unittest
 {
-    UnsignedStringBuf buf;
+    UnsignedStringBuf buf = void;
     assert(0.unsignedToTempString(buf) == "0");
     assert(1.unsignedToTempString(buf) == "1");
     assert(12.unsignedToTempString(buf) == "12");
     assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf");
+    assert(0x12ABCF .unsignedToTempString!(16, true)(buf) == "12ABCF");
     assert(long.sizeof.unsignedToTempString(buf) == "8");
     assert(uint.max.unsignedToTempString(buf) == "4294967295");
     assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615");
@@ -106,16 +138,17 @@ unittest
     // test bad radices
     assert(!is(typeof(100.unsignedToTempString!1(buf))));
     assert(!is(typeof(100.unsignedToTempString!0(buf) == "")));
+    assert(!is(typeof(100.unsignedToTempString!37(buf) == "")));
 }
 
-alias SignedStringBuf = char[20];
+alias SignedStringBuf = char[65];
 
-char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe
+T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(long value, return scope T[] buf)
 {
     bool neg = value < 0;
     if (neg)
         value = cast(ulong)-value;
-    auto r = unsignedToTempString!radix(value, buf);
+    auto r = unsignedToTempString!(radix, upperCase)(value, buf);
     if (neg)
     {
         // about to do a slice without a bounds check
@@ -126,7 +159,7 @@ char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf)
     return r;
 }
 
-auto signedToTempString(uint radix = 10)(long value) @safe
+auto signedToTempString(uint radix = 10)(long value)
 {
     bool neg = value < 0;
     if (neg)
@@ -142,7 +175,7 @@ auto signedToTempString(uint radix = 10)(long value) @safe
 
 unittest
 {
-    SignedStringBuf buf;
+    SignedStringBuf buf = void;
     assert(0.signedToTempString(buf) == "0");
     assert(1.signedToTempString(buf) == "1");
     assert((-1).signedToTempString(buf) == "-1");
@@ -150,6 +183,7 @@ unittest
     assert((-12).signedToTempString(buf) == "-12");
     assert(0x12ABCF .signedToTempString!16(buf) == "12abcf");
     assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf");
+    assert((-0x12ABCF) .signedToTempString!(16, true)(buf) == "-12ABCF");
     assert(long.sizeof.signedToTempString(buf) == "8");
     assert(int.max.signedToTempString(buf) == "2147483647");
     assert(int.min.signedToTempString(buf) == "-2147483648");
@@ -183,7 +217,7 @@ unittest
  * Returns:
  *      number of digits
  */
-int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36)
+int numDigits(uint radix = 10)(ulong value) if (radix >= 2 && radix <= 36)
 {
      int n = 1;
      while (1)
index c85682ec0a6b8651b33b47542337198500f71ad2..ca833eaac33083ce8e7f871993a31d8748e2a9bd 100644 (file)
@@ -281,11 +281,185 @@ else
     alias cpp_ptrdiff_t = ptrdiff_t;
 }
 
-// ABI layout of native complex types.
-private struct _Complex(T)
+/** ABI layout of native complex types.
+ */
+struct _Complex(T)
+    if (is(T == float) || is(T == double) || is(T == c_long_double))
 {
-    T re;
-    T im;
+    T re = 0;
+    T im = 0;
+
+    // Construction
+/+ https://issues.dlang.org/show_bug.cgi?id=23788 dmd codegen problem with constructors and _Complex!float
+    this(_Complex!float  c) { re = c.re; im = c.im; }
+    this(_Complex!double c) { re = c.re; im = c.im; }
+    this(_Complex!c_long_double   c) { re = c.re; im = c.im; }
+
+    this(T re, T im) { this.re = re; this.im = im; }
+
+    this(T re) { this.re = re; this.im = 0; }
++/
+    // Cast
+    R opCast(R)()
+        if (is(R == _Complex!float) || is(R == _Complex!double) || is(R == _Complex!c_long_double))
+    {
+        return R(this.re, this.im);
+    }
+
+    // Assignment
+
+    ref _Complex opAssign(_Complex!float  c) { re = c.re; im = c.im; return this; }
+    ref _Complex opAssign(_Complex!double c) { re = c.re; im = c.im; return this; }
+    ref _Complex opAssign(_Complex!c_long_double   c) { re = c.re; im = c.im; return this; }
+
+    ref _Complex opAssign(T t) { re = t; im = 0; return this; }
+
+    // Equals
+
+    bool opEquals(_Complex!float  c) { return re == c.re && im == c.im; }
+    bool opEquals(_Complex!double c) { return re == c.re && im == c.im; }
+    bool opEquals(_Complex!c_long_double   c) { return re == c.re && im == c.im; }
+
+    bool opEquals(T t) { return re == t && im == 0; }
+
+    // Unary operators
+
+    // +complex
+    _Complex opUnary(string op)()
+        if (op == "+")
+    {
+        return this;
+    }
+
+    // -complex
+    _Complex opUnary(string op)()
+        if (op == "-")
+    {
+        return _Complex(-re, -im);
+    }
+
+    // BINARY OPERATORS
+
+    // complex op complex
+    _Complex!(CommonType!(T,R)) opBinary(string op, R)(_Complex!R z)
+    {
+        alias C = typeof(return);
+        auto w = C(this.re, this.im);
+        return w.opOpAssign!(op)(z);
+    }
+
+    // complex op numeric
+    _Complex!(CommonType!(T,R)) opBinary(string op, R)(R r)
+        if (is(R : c_long_double))
+    {
+        alias C = typeof(return);
+        auto w = C(this.re, this.im);
+        return w.opOpAssign!(op)(r);
+    }
+
+    // numeric + complex,  numeric * complex
+    _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r)
+        if ((op == "+" || op == "*") && is(R : c_long_double))
+    {
+        return opBinary!(op)(r);
+    }
+
+    // numeric - complex
+    _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r)
+        if (op == "-" && is(R : c_long_double))
+    {
+        return _Complex(r - re, -im);
+    }
+
+    // numeric / complex
+    _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r)
+        if (op == "/" && is(R : c_long_double))
+    {
+        import core.math : fabs;
+        typeof(return) w = void;
+        if (fabs(re) < fabs(im))
+        {
+            immutable ratio = re/im;
+            immutable rdivd = r/(re*ratio + im);
+
+            w.re = rdivd*ratio;
+            w.im = -rdivd;
+        }
+        else
+        {
+            immutable ratio = im/re;
+            immutable rdivd = r/(re + im*ratio);
+
+            w.re = rdivd;
+            w.im = -rdivd*ratio;
+        }
+
+        return w;
+    }
+
+    // OP-ASSIGN OPERATORS
+
+    // complex += complex,  complex -= complex
+    ref _Complex opOpAssign(string op, C)(C z)
+        if ((op == "+" || op == "-") && is(C R == _Complex!R))
+    {
+        mixin ("re "~op~"= z.re;");
+        mixin ("im "~op~"= z.im;");
+        return this;
+    }
+
+    // complex *= complex
+    ref _Complex opOpAssign(string op, C)(C z)
+        if (op == "*" && is(C R == _Complex!R))
+    {
+        auto temp = re*z.re - im*z.im;
+        im = im*z.re + re*z.im;
+        re = temp;
+        return this;
+    }
+
+    // complex /= complex
+    ref _Complex opOpAssign(string op, C)(C z)
+        if (op == "/" && is(C R == _Complex!R))
+    {
+        import core.math : fabs;
+        if (fabs(z.re) < fabs(z.im))
+        {
+            immutable ratio = z.re/z.im;
+            immutable denom = z.re*ratio + z.im;
+
+            immutable temp = (re*ratio + im)/denom;
+            im = (im*ratio - re)/denom;
+            re = temp;
+        }
+        else
+        {
+            immutable ratio = z.im/z.re;
+            immutable denom = z.re + z.im*ratio;
+
+            immutable temp = (re + im*ratio)/denom;
+            im = (im - re*ratio)/denom;
+            re = temp;
+        }
+        return this;
+    }
+
+    // complex += numeric,  complex -= numeric
+    ref _Complex opOpAssign(string op, U : T)(U a)
+        if (op == "+" || op == "-")
+    {
+        mixin ("re "~op~"= a;");
+        return this;
+    }
+
+    // complex *= numeric,  complex /= numeric
+    ref _Complex opOpAssign(string op, U : T)(U a)
+        if (op == "*" || op == "/")
+    {
+        mixin ("re "~op~"= a;");
+        mixin ("im "~op~"= a;");
+        return this;
+    }
 
     // Helper properties.
     pragma(inline, true)
@@ -311,6 +485,168 @@ enum __c_complex_real   : _Complex!c_long_double;
 alias c_complex_float = __c_complex_float;
 alias c_complex_double = __c_complex_double;
 alias c_complex_real = __c_complex_real;
+
+private template CommonType(T, R)
+{
+    // Special kludge for Microsoft c_long_double
+    static if (is(T == c_long_double))
+        alias CommonType = T;
+    else static if (is(R == c_long_double))
+        alias CommonType = R;
+    else
+        alias CommonType = typeof(true ? T.init : R.init);
+}
+
+/************ unittests ****************/
+
+version (unittest)
+{
+  private:
+
+    alias _cfloat  = _Complex!float;
+    alias _cdouble = _Complex!double;
+    alias _creal   = _Complex!c_long_double;
+
+    T abs(T)(T t) => t < 0 ? -t : t;
+}
+
+@safe pure nothrow unittest
+{
+    auto c1 = _cdouble(1.0, 1.0);
+
+    // Check unary operations.
+    auto c2 = _cdouble(0.5, 2.0);
+
+    assert(c2 == +c2);
+
+    assert((-c2).re == -(c2.re));
+    assert((-c2).im == -(c2.im));
+    assert(c2 == -(-c2));
+
+    // Check complex-complex operations.
+    auto cpc = c1 + c2;
+    assert(cpc.re == c1.re + c2.re);
+    assert(cpc.im == c1.im + c2.im);
+
+    auto cmc = c1 - c2;
+    assert(cmc.re == c1.re - c2.re);
+    assert(cmc.im == c1.im - c2.im);
+
+    auto ctc = c1 * c2;
+    assert(ctc == _cdouble(-1.5, 2.5));
+
+    auto cdc = c1 / c2;
+    assert(abs(cdc.re -  0.5882352941177) < 1e-12);
+    assert(abs(cdc.im - -0.3529411764706) < 1e-12);
+
+    // Check complex-real operations.
+    double a = 123.456;
+
+    auto cpr = c1 + a;
+    assert(cpr.re == c1.re + a);
+    assert(cpr.im == c1.im);
+
+    auto cmr = c1 - a;
+    assert(cmr.re == c1.re - a);
+    assert(cmr.im == c1.im);
+
+    auto ctr = c1 * a;
+    assert(ctr.re == c1.re*a);
+    assert(ctr.im == c1.im*a);
+
+    auto cdr = c1 / a;
+    assert(abs(cdr.re - 0.00810005184033) < 1e-12);
+    assert(abs(cdr.im - 0.00810005184033) < 1e-12);
+
+    auto rpc = a + c1;
+    assert(rpc == cpr);
+
+    auto rmc = a - c1;
+    assert(rmc.re == a-c1.re);
+    assert(rmc.im == -c1.im);
+
+    auto rtc = a * c1;
+    assert(rtc == ctr);
+
+    auto rdc = a / c1;
+    assert(abs(rdc.re -  61.728) < 1e-12);
+    assert(abs(rdc.im - -61.728) < 1e-12);
+
+    rdc = a / c2;
+    assert(abs(rdc.re -  14.5242352941) < 1e-10);
+    assert(abs(rdc.im - -58.0969411765) < 1e-10);
+
+    // Check operations between different complex types.
+    auto cf = _cfloat(1.0, 1.0);
+    auto cr = _creal(1.0, 1.0);
+    auto c1pcf = c1 + cf;
+    auto c1pcr = c1 + cr;
+    static assert(is(typeof(c1pcf) == _cdouble));
+    static assert(is(typeof(c1pcr) == _creal));
+    assert(c1pcf.re == c1pcr.re);
+    assert(c1pcf.im == c1pcr.im);
+
+    auto c1c = c1;
+    auto c2c = c2;
+
+    c1c /= c1;
+    assert(abs(c1c.re - 1.0) < 1e-10);
+    assert(abs(c1c.im - 0.0) < 1e-10);
+
+    c1c = c1;
+    c1c /= c2;
+    assert(abs(c1c.re -  0.5882352941177) < 1e-12);
+    assert(abs(c1c.im - -0.3529411764706) < 1e-12);
+
+    c2c /= c1;
+    assert(abs(c2c.re -  1.25) < 1e-11);
+    assert(abs(c2c.im -  0.75) < 1e-12);
+
+    c2c = c2;
+    c2c /= c2;
+    assert(abs(c2c.re -  1.0) < 1e-11);
+    assert(abs(c2c.im -  0.0) < 1e-12);
+}
+
+@safe pure nothrow unittest
+{
+    // Initialization
+    _cdouble a = _cdouble(1, 0);
+    assert(a.re == 1 && a.im == 0);
+    _cdouble b = _cdouble(1.0, 0);
+    assert(b.re == 1.0 && b.im == 0);
+//    _cdouble c = _creal(1.0, 2);
+//    assert(c.re == 1.0 && c.im == 2);
+}
+
+@safe pure nothrow unittest
+{
+    // Assignments and comparisons
+    _cdouble z;
+
+    z = 1;
+    assert(z == 1);
+    assert(z.re == 1.0  &&  z.im == 0.0);
+
+    z = 2.0;
+    assert(z == 2.0);
+    assert(z.re == 2.0  &&  z.im == 0.0);
+
+    z = 1.0L;
+    assert(z == 1.0L);
+    assert(z.re == 1.0  &&  z.im == 0.0);
+
+    auto w = _creal(1.0, 1.0);
+    z = w;
+    assert(z == w);
+    assert(z.re == 1.0  &&  z.im == 1.0);
+
+    auto c = _cfloat(2.0, 2.0);
+    z = c;
+    assert(z == c);
+    assert(z.re == 2.0  &&  z.im == 2.0);
+}
+
 }
 
 
index ddd04ae0576f24af9b56bb8adb3c754eb705ff11..afcfd744f0a81ee0d058ab94a41584b2fb14be68 100644 (file)
@@ -84,7 +84,8 @@ class Condition
     /// ditto
     this( shared Mutex m ) shared nothrow @safe @nogc
     {
-        this(m, true);
+        import core.atomic : atomicLoad;
+        this(atomicLoad(m), true);
     }
 
     //
@@ -117,7 +118,15 @@ class Condition
         }
         else version (Posix)
         {
-            m_assocMutex = m;
+            static if (is(Q == shared))
+            {
+                import core.atomic : atomicLoad;
+                m_assocMutex = atomicLoad(m);
+            }
+            else
+            {
+                m_assocMutex = m;
+            }
             static if ( is( typeof( pthread_condattr_setclock ) ) )
             {
                 () @trusted
@@ -183,7 +192,8 @@ class Condition
     /// ditto
     @property shared(Mutex) mutex() shared
     {
-        return m_assocMutex;
+        import core.atomic : atomicLoad;
+        return atomicLoad(m_assocMutex);
     }
 
     // undocumented function for internal use
@@ -195,7 +205,8 @@ class Condition
     // ditto
     final @property shared(Mutex) mutex_nothrow() shared pure nothrow @safe @nogc
     {
-        return m_assocMutex;
+        import core.atomic : atomicLoad;
+        return atomicLoad(m_assocMutex);
     }
 
     ////////////////////////////////////////////////////////////////////////////
index 71b0237c1149394e9e3cf2d56b6431dd950e7b9d..d81ebbdcc824ba849c0663250e1bfbe13f318b7f 100644 (file)
@@ -18,3 +18,42 @@ public import core.thread.threadbase;
 public import core.thread.threadgroup;
 public import core.thread.types;
 public import core.thread.context;
+
+
+// this test is here to avoid a cyclic dependency between
+// core.thread and core.atomic
+unittest
+{
+    import core.atomic;
+
+    // Use heap memory to ensure an optimizing
+    // compiler doesn't put things in registers.
+    uint* x = new uint();
+    bool* f = new bool();
+    uint* r = new uint();
+
+    auto thr = new Thread(()
+    {
+        while (!*f)
+        {
+        }
+
+        atomicFence();
+
+        *r = *x;
+    });
+
+    thr.start();
+
+    *x = 42;
+
+    atomicFence();
+
+    *f = true;
+
+    atomicFence();
+
+    thr.join();
+
+    assert(*r == 42);
+}
index eb84ad74b48142c2f2c95458d62f2001e1c7c7ac..998f610c2556df60908cd12850eb3fcbc82322ac 100644 (file)
@@ -41,8 +41,9 @@ version (GNU)
 }
 else
 {
-    // this should be true for most architectures
-    enum isStackGrowingDown = true;
+    version (X86) enum isStackGrowingDown = true;
+    else version (X86_64) enum isStackGrowingDown = true;
+    else static assert(0, "It is undefined how the stack grows on this architecture.");
 }
 
 package
index 8d508755c7dada490aa34c3b6105e2c7fa1dbffd..be941e2abcd817d3955dc7490ab754fab8dd0aad 100644 (file)
@@ -18,7 +18,7 @@
     $(LEADINGROW Types)
     $(TR $(TDNW $(LREF Duration)) $(TD Represents a duration of time of weeks
     or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).))
-    $(TR $(TDNW $(LREF TickDuration)) $(TD Represents a duration of time in
+    $(TR $(TDNW $(LREF TickDuration)) $(TD $(RED DEPRECATED) Represents a duration of time in
     system clock ticks, using the highest precision that the system provides.))
     $(TR $(TDNW $(LREF MonoTime)) $(TD Represents a monotonic timestamp in
     system clock ticks, using the highest precision that the system provides.))
@@ -682,21 +682,21 @@ public:
         $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration))
         $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration))
         $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration))
-        $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration))
-        $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration))
         )
 
         Params:
             rhs = The duration to add to or subtract from this $(D Duration).
       +/
-    Duration opBinary(string op, D)(D rhs) const nothrow @nogc
-        if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) ||
-           ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)))
+    Duration opBinary(string op)(const Duration rhs) const nothrow @nogc
+        if (op == "+" || op == "-" || op == "%")
     {
-        static if (is(immutable D == immutable Duration))
-            return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs"));
-        else
-            return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs"));
+        return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs"));
+    }
+
+    deprecated Duration opBinary(string op)(const TickDuration rhs) const nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs"));
     }
 
     version (CoreUnittest) unittest
@@ -733,7 +733,13 @@ public:
                 assert((cast(D)Duration(-7)) - (cast(E)Duration(-5)) == Duration(-2));
                 assert((cast(D)Duration(-7)) % (cast(E)Duration(5)) == Duration(-2));
             }
+        }
+    }
 
+    version (CoreUnittest) deprecated unittest
+    {
+        foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
+        {
             foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
             {
                 assertApprox((cast(D)Duration(5)) + cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80));
@@ -761,6 +767,8 @@ public:
 
 
     /++
+        $(RED TickDuration is Deprecated)
+
         Adds or subtracts two durations.
 
         The legal types of arithmetic for $(D Duration) using this operator are
@@ -774,14 +782,14 @@ public:
             lhs = The $(D TickDuration) to add to this $(D Duration) or to
                   subtract this $(D Duration) from.
       +/
-    Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc
+    deprecated Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc
         if ((op == "+" || op == "-") &&
             is(immutable D == immutable TickDuration))
     {
         return Duration(mixin("lhs.hnsecs " ~ op ~ " _hnsecs"));
     }
 
-    version (CoreUnittest) unittest
+    version (CoreUnittest) deprecated unittest
     {
         foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
         {
@@ -821,21 +829,22 @@ public:
         $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration))
         $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration))
         $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration))
-        $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration))
-        $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration))
         )
 
         Params:
             rhs = The duration to add to or subtract from this $(D Duration).
       +/
-    ref Duration opOpAssign(string op, D)(const scope D rhs) nothrow @nogc
-        if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) ||
-           ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)))
+    ref Duration opOpAssign(string op)(const Duration rhs) nothrow @nogc
+        if (op == "+" || op == "-" || op == "%")
     {
-        static if (is(immutable D == immutable Duration))
-            mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;");
-        else
-            mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;");
+        mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;");
+        return this;
+    }
+
+    deprecated ref Duration opOpAssign(string op)(const TickDuration rhs) nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;");
         return this;
     }
 
@@ -850,13 +859,6 @@ public:
                 throw new AssertError("op assign failed", __FILE__, line);
         }
 
-        static void test2(string op, E)
-                         (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__)
-        {
-            assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line);
-            assertApprox(actual, lower, upper, "op assign failed", line);
-        }
-
         foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
         {
             test1!"+="(Duration(5), (cast(E)Duration(7)), Duration(12));
@@ -888,6 +890,26 @@ public:
             test1!"%="(Duration(-7), (cast(E)Duration(-5)), Duration(-2));
         }
 
+        foreach (D; AliasSeq!(const Duration, immutable Duration))
+        {
+            foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
+            {
+                D lhs = D(120);
+                E rhs = E(120);
+                static assert(!__traits(compiles, lhs += rhs), D.stringof ~ " " ~ E.stringof);
+            }
+        }
+    }
+
+    version (CoreUnittest) deprecated unittest
+    {
+        static void test2(string op, E)
+                         (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__)
+        {
+            assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line);
+            assertApprox(actual, lower, upper, "op assign failed", line);
+        }
+
         foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
         {
             test2!"+="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80));
@@ -913,8 +935,7 @@ public:
 
         foreach (D; AliasSeq!(const Duration, immutable Duration))
         {
-            foreach (E; AliasSeq!(Duration, const Duration, immutable Duration,
-                                   TickDuration, const TickDuration, immutable TickDuration))
+            foreach (E; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
             {
                 D lhs = D(120);
                 E rhs = E(120);
@@ -1170,19 +1191,21 @@ public:
 
 
     /++
+        $(RED TickDuration is Deprecated)
+
         Returns a $(LREF TickDuration) with the same number of hnsecs as this
         $(D Duration).
         Note that the conventional way to convert between $(D Duration) and
         $(D TickDuration) is using $(REF to, std,conv), e.g.:
         $(D duration.to!TickDuration())
       +/
-    TickDuration opCast(T)() const nothrow @nogc
+    deprecated TickDuration opCast(T)() const nothrow @nogc
         if (is(immutable T == immutable TickDuration))
     {
         return TickDuration.from!"hnsecs"(_hnsecs);
     }
 
-    version (CoreUnittest) unittest
+    version (CoreUnittest) deprecated unittest
     {
         foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
         {
@@ -1762,6 +1785,8 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest
 }
 
 /++
+    $(RED TickDuration is DEPRECATED)
+
     Converts a $(D TickDuration) to the given units as either an integral
     value or a floating point value.
 
@@ -1773,6 +1798,7 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest
 
         td    = The TickDuration to convert
   +/
+deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead")
 T to(string units, T, D)(D td) @safe pure nothrow @nogc
     if (is(immutable D == immutable TickDuration) &&
        (units == "seconds" ||
@@ -1804,7 +1830,7 @@ T to(string units, T, D)(D td) @safe pure nothrow @nogc
 }
 
 ///
-unittest
+deprecated unittest
 {
     auto t = TickDuration.from!"seconds"(1000);
 
@@ -1816,7 +1842,7 @@ unittest
     assert(fabs(td - 1000) < 0.001);
 }
 
-unittest
+deprecated unittest
 {
     void testFun(string U)() {
         auto t1v = 1000;
@@ -2756,22 +2782,24 @@ unittest
 
 
 /++
-    $(RED Warning: TickDuration will be deprecated in the near future (once all
-          uses of it in Phobos have been deprecated). Please use
+   $(RED Warning: TickDuration is deprecated. Please use
           $(LREF MonoTime) for the cases where a monotonic timestamp is needed
           and $(LREF Duration) when a duration is needed, rather than using
-          TickDuration. It has been decided that TickDuration is too confusing
-          (e.g. it conflates a monotonic timestamp and a duration in monotonic
-           clock ticks) and that having multiple duration types is too awkward
-          and confusing.)
+          TickDuration.)
 
    Represents a duration of time in system clock ticks.
 
    The system clock ticks are the ticks of the system clock at the highest
    precision that the system provides.
   +/
+deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead")
 struct TickDuration
 {
+deprecated:
+    private static TickDuration TDRvalueOf(TickDuration td)
+    {
+        return td;
+    }
     /++
        The number of ticks that the system clock has in one second.
 
@@ -2811,14 +2839,14 @@ struct TickDuration
 
     version (CoreUnittest) unittest
     {
-        assert(zero == TickDuration(0));
-        assert(TickDuration.max == TickDuration(long.max));
-        assert(TickDuration.min == TickDuration(long.min));
-        assert(TickDuration.min < TickDuration.zero);
-        assert(TickDuration.zero < TickDuration.max);
-        assert(TickDuration.min < TickDuration.max);
-        assert(TickDuration.min - TickDuration(1) == TickDuration.max);
-        assert(TickDuration.max + TickDuration(1) == TickDuration.min);
+        assert((zero == TickDuration(0)) == true);
+        assert((TickDuration.max == TickDuration(long.max)) == true);
+        assert((TickDuration.min == TickDuration(long.min)) == true);
+        assert((TickDuration.min < TickDuration.zero) == true);
+        assert((TickDuration.zero < TickDuration.max) == true);
+        assert((TickDuration.min < TickDuration.max) == true);
+        assert((TickDuration.min - TickDuration(1) == TickDuration.max) == true);
+        assert((TickDuration.max + TickDuration(1) == TickDuration.min) == true);
     }
 
 
@@ -3040,12 +3068,12 @@ struct TickDuration
         {
             auto a = TickDuration.currSystemTick;
             auto result = a += cast(T)TickDuration.currSystemTick;
-            assert(a == result);
+            assert((a == result) == true);
             assert(a.to!("seconds", real)() >= 0);
 
             auto b = TickDuration.currSystemTick;
             result = b -= cast(T)TickDuration.currSystemTick;
-            assert(b == result);
+            assert((b == result) == true);
             assert(b.to!("seconds", real)() <= 0);
 
             foreach (U; AliasSeq!(const TickDuration, immutable TickDuration))
@@ -3104,11 +3132,11 @@ struct TickDuration
     {
         foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
         {
-            assert(-(cast(T)TickDuration(7)) == TickDuration(-7));
-            assert(-(cast(T)TickDuration(5)) == TickDuration(-5));
-            assert(-(cast(T)TickDuration(-7)) == TickDuration(7));
-            assert(-(cast(T)TickDuration(-5)) == TickDuration(5));
-            assert(-(cast(T)TickDuration(0)) == TickDuration(0));
+            assert((-(cast(T)TickDuration(7)) == TickDuration(-7)) == true);
+            assert((-(cast(T)TickDuration(5)) == TickDuration(-5)) == true);
+            assert((-(cast(T)TickDuration(-7)) == TickDuration(7)) == true);
+            assert((-(cast(T)TickDuration(-5)) == TickDuration(5)) == true);
+            assert((-(cast(T)TickDuration(0)) == TickDuration(0)) == true);
         }
     }
 
@@ -3130,9 +3158,9 @@ struct TickDuration
             {
                 T t = TickDuration.currSystemTick;
                 U u = t;
-                assert(t == u);
-                assert(rvalueOf(t) == u);
-                assert(t == rvalueOf(u));
+                assert((t == u) == true);
+                assert((TDRvalueOf(t) == u) == true);
+                assert((t == TDRvalueOf(u)) == true);
             }
         }
 
@@ -3142,20 +3170,20 @@ struct TickDuration
             {
                 T t = TickDuration.currSystemTick;
                 U u = t + t;
-                assert(t < u);
-                assert(t <= t);
-                assert(u > t);
-                assert(u >= u);
-
-                assert(rvalueOf(t) < u);
-                assert(rvalueOf(t) <= t);
-                assert(rvalueOf(u) > t);
-                assert(rvalueOf(u) >= u);
-
-                assert(t < rvalueOf(u));
-                assert(t <= rvalueOf(t));
-                assert(u > rvalueOf(t));
-                assert(u >= rvalueOf(u));
+                assert((t < u) == true);
+                assert((t <= t) == true);
+                assert((u > t) == true);
+                assert((u >= u) == true);
+
+                assert((TDRvalueOf(t) < u) == true);
+                assert((TDRvalueOf(t) <= t) == true);
+                assert((TDRvalueOf(u) > t) == true);
+                assert((TDRvalueOf(u) >= u) == true);
+
+                assert((t < TDRvalueOf(u)) == true);
+                assert((t <= TDRvalueOf(t)) == true);
+                assert((u > TDRvalueOf(t)) == true);
+                assert((u >= TDRvalueOf(u)) == true);
             }
         }
     }
@@ -3186,7 +3214,7 @@ struct TickDuration
         TickDuration t1 = curr;
         immutable t2 = curr + curr;
         t1 *= 2;
-        assert(t1 == t2);
+        assert((t1 == t2) == true);
 
         t1 = curr;
         t1 *= 2.0;
@@ -3195,7 +3223,7 @@ struct TickDuration
 
         t1 = curr;
         t1 *= 2.1;
-        assert(t1 > t2);
+        assert((t1 > t2) == true);
 
         foreach (T; AliasSeq!(const TickDuration, immutable TickDuration))
         {
@@ -3237,7 +3265,7 @@ struct TickDuration
         immutable t1 = curr;
         TickDuration t2 = curr + curr;
         t2 /= 2;
-        assert(t1 == t2);
+        assert((t1 == t2) == true);
 
         t2 = curr + curr;
         t2 /= 2.0;
@@ -3246,7 +3274,7 @@ struct TickDuration
 
         t2 = curr + curr;
         t2 /= 2.1;
-        assert(t1 > t2);
+        assert((t1 > t2) == true);
 
         _assertThrown!TimeException(t2 /= 0);
 
@@ -3284,10 +3312,10 @@ struct TickDuration
         {
             T t1 = TickDuration.currSystemTick;
             T t2 = t1 + t1;
-            assert(t1 * 2 == t2);
+            assert((t1 * 2 == t2) == true);
             immutable tol = TickDuration(cast(long)(_abs(t1.length) * double.epsilon * 2.0));
             assertApprox(t1 * 2.0, t2 - tol, t2 + tol);
-            assert(t1 * 2.1 > t2);
+            assert((t1 * 2.1 > t2) == true);
         }
     }
 
@@ -3323,12 +3351,12 @@ struct TickDuration
         {
             T t1 = TickDuration.currSystemTick;
             T t2 = t1 + t1;
-            assert(t2 / 2 == t1);
+            assert((t2 / 2 == t1) == true);
             immutable tol = TickDuration(cast(long)(_abs(t2.length) * double.epsilon / 2.0));
             assertApprox(t2 / 2.0, t1 - tol, t1 + tol);
-            assert(t2 / 2.1 < t1);
+            assert((t2 / 2.1 < t1) == true);
 
-            _assertThrown!TimeException(t2 / 0);
+            _assertThrownDep!TimeException(t2 / 0);
         }
     }
 
@@ -3430,7 +3458,6 @@ struct TickDuration
     }
 }
 
-
 /++
     Generic way of converting between two time units. Conversions to smaller
     units use truncating division. Years and months can be converted to each
@@ -3641,6 +3668,7 @@ Duration abs(Duration duration) @safe pure nothrow @nogc
 }
 
 /++ Ditto +/
+deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead")
 TickDuration abs(TickDuration duration) @safe pure nothrow @nogc
 {
     return TickDuration(_abs(duration.length));
@@ -3650,9 +3678,12 @@ unittest
 {
     assert(abs(dur!"msecs"(5)) == dur!"msecs"(5));
     assert(abs(dur!"msecs"(-5)) == dur!"msecs"(5));
+}
 
-    assert(abs(TickDuration(17)) == TickDuration(17));
-    assert(abs(TickDuration(-17)) == TickDuration(17));
+deprecated unittest
+{
+    assert((abs(TickDuration(17)) == TickDuration(17)) == true);
+    assert((abs(TickDuration(-17)) == TickDuration(17)) == true);
 }
 
 
@@ -3987,6 +4018,28 @@ unittest
     }
 }
 
+version (CoreUnittest) deprecated void _assertThrownDep(T : Throwable = Exception, E)
+                                    (lazy E expression,
+                                     string msg = null,
+                                     string file = __FILE__,
+                                     size_t line = __LINE__)
+{
+    bool thrown = false;
+
+    try
+        expression();
+    catch (T t)
+        thrown = true;
+
+    if (!thrown)
+    {
+        immutable tail = msg.length == 0 ? "." : ": " ~ msg;
+
+        throw new AssertError("assertThrown() failed: No " ~ T.stringof ~ " was thrown" ~ tail, file, line);
+    }
+}
+
+
 
 version (CoreUnittest) void assertApprox(D, E)(D actual,
                                           E lower,
@@ -4001,7 +4054,7 @@ version (CoreUnittest) void assertApprox(D, E)(D actual,
         throw new AssertError(msg ~ ": upper: " ~ actual.toString(), __FILE__, line);
 }
 
-version (CoreUnittest) void assertApprox(D, E)(D actual,
+version (CoreUnittest) deprecated void assertApprox(D, E)(D actual,
                                           E lower,
                                           E upper,
                                           string msg = "unittest failure",
index 0c7a771b167bc036a4f04de88ff4f9ebb408065c..5376957befdfb6137e2799f27bc387c33dcdacd6 100644 (file)
@@ -737,7 +737,7 @@ version (Shared)
         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
     }
 
-    void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
+    void getDependencies(const ref dl_phdr_info info, ref Array!(DSO*) deps)
     {
         // get the entries of the .dynamic section
         ElfW!"Dyn"[] dyns;
@@ -833,7 +833,7 @@ version (Shared)
  * Scan segments in Linux dl_phdr_info struct and store
  * the TLS and writeable data segments in *pdso.
  */
-void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
+void scanSegments(const ref dl_phdr_info info, DSO* pdso) nothrow @nogc
 {
     foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
     {
@@ -957,7 +957,7 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
  * Determine if 'addr' lies within shared object 'info'.
  * If so, return true and fill in 'result' with the corresponding ELF program header.
  */
-bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
+bool findSegmentForAddr(const ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
 {
     if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject
         return false;
index a77788bec20deb1b141fca2108879fdd07b0a914..610cb7a30a1f82d5fab1a0cf5162c7d381a90d81 100644 (file)
@@ -4529,12 +4529,15 @@ public import core.internal.entrypoint : _d_cmain;
 
 public import core.internal.array.appending : _d_arrayappendT;
 version (D_ProfileGC)
+{
     public import core.internal.array.appending : _d_arrayappendTTrace;
+    public import core.internal.array.concatenation : _d_arraycatnTXTrace;
+}
 public import core.internal.array.appending : _d_arrayappendcTXImpl;
 public import core.internal.array.comparison : __cmp;
 public import core.internal.array.equality : __equals;
 public import core.internal.array.casting: __ArrayCast;
-public import core.internal.array.concatenation : _d_arraycatnTXImpl;
+public import core.internal.array.concatenation : _d_arraycatnTX;
 public import core.internal.array.construction : _d_arrayctor;
 public import core.internal.array.construction : _d_arraysetctor;
 public import core.internal.array.arrayassign : _d_arrayassign_l;
index 5d5ddb34740b691e8687687d61b50a1090fadb6d..c59d9dc1234abb07d5fe7809267c9d2db567d3c0 100644 (file)
@@ -71,7 +71,7 @@ Params:
 Returns:
     non-zero when the loop was exited through a `break`
 */
-extern (C) int _aApplycd1(in char[] aa, dg_t dg)
+extern (C) int _aApplycd1(scope const(char)[] aa, dg_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -132,7 +132,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplywd1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplywd1(scope const(wchar)[] aa, dg_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -193,7 +193,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplycw1(in char[] aa, dg_t dg)
+extern (C) int _aApplycw1(scope const(char)[] aa, dg_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -267,7 +267,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplywc1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplywc1(scope const(wchar)[] aa, dg_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -347,7 +347,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplydc1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplydc1(scope const(dchar)[] aa, dg_t dg)
 {
     int result;
 
@@ -423,7 +423,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplydw1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplydw1(scope const(dchar)[] aa, dg_t dg)
 {
     int result;
 
@@ -508,7 +508,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c);
 /**
 Variants of _aApplyXXX that include a loop index.
 */
-extern (C) int _aApplycd2(in char[] aa, dg2_t dg)
+extern (C) int _aApplycd2(scope const(char)[] aa, dg2_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -576,7 +576,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplywd2(scope const(wchar)[] aa, dg2_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -644,7 +644,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplycw2(in char[] aa, dg2_t dg)
+extern (C) int _aApplycw2(scope const(char)[] aa, dg2_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -723,7 +723,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplywc2(scope const(wchar)[] aa, dg2_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -808,7 +808,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplydc2(scope const(dchar)[] aa, dg2_t dg)
 {
     int result;
     size_t len = aa.length;
@@ -888,7 +888,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplydw2(scope const(dchar)[] aa, dg2_t dg)
 {   int result;
 
     debug(apply) printf("_aApplydw2(), len = %d\n", aa.length);
index ce3bb9eaf705c0f264c497f213559a15309fb04d..560025c636d8ce1858eff91ccd9e291e9c28aabd 100644 (file)
@@ -34,7 +34,7 @@ Params:
 Returns:
     non-zero when the loop was exited through a `break`
 */
-extern (C) int _aApplyRcd1(in char[] aa, dg_t dg)
+extern (C) int _aApplyRcd1(scope const(char)[] aa, dg_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length);
@@ -107,7 +107,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplyRwd1(scope const(wchar)[] aa, dg_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length);
@@ -170,7 +170,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRcw1(in char[] aa, dg_t dg)
+extern (C) int _aApplyRcw1(scope const(char)[] aa, dg_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length);
@@ -256,7 +256,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplyRwc1(scope const(wchar)[] aa, dg_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length);
@@ -340,7 +340,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplyRdc1(scope const(dchar)[] aa, dg_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length);
@@ -418,7 +418,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplyRdw1(scope const(dchar)[] aa, dg_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length);
@@ -502,7 +502,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c);
 /**
 Variants of _aApplyRXXX that include a loop index.
 */
-extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg)
+extern (C) int _aApplyRcd2(scope const(char)[] aa, dg2_t dg)
 {   int result;
     size_t i;
     size_t len = aa.length;
@@ -578,7 +578,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplyRwd2(scope const(wchar)[] aa, dg2_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length);
@@ -643,7 +643,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg)
+extern (C) int _aApplyRcw2(scope const(char)[] aa, dg2_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length);
@@ -731,7 +731,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplyRwc2(scope const(wchar)[] aa, dg2_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length);
@@ -817,7 +817,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplyRdc2(scope const(dchar)[] aa, dg2_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length);
@@ -896,7 +896,7 @@ unittest
 }
 
 /// ditto
-extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplyRdw2(scope const(dchar)[] aa, dg2_t dg)
 {   int result;
 
     debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length);
index f2515c3c6776264f893ef71e6aa0e91994bad061..a37541bf01fe6b418a6cc8f013b646b0dc497d2d 100644 (file)
@@ -1197,7 +1197,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
 }
 
 /// ditto
-extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
+extern (C) void* _d_newitemT(const TypeInfo _ti) pure nothrow @weak
 {
     import core.stdc.string;
     auto p = _d_newitemU(_ti);
@@ -1206,7 +1206,7 @@ extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
 }
 
 /// Same as above, for item with non-zero initializer.
-extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak
+extern (C) void* _d_newitemiT(const TypeInfo _ti) pure nothrow @weak
 {
     import core.stdc.string;
     auto p = _d_newitemU(_ti);
@@ -1286,7 +1286,7 @@ extern (C) CollectHandler rt_getCollectHandler()
 /**
  *
  */
-extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow
+extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow
 {
     if (attr & BlkAttr.STRUCTFINAL)
     {
@@ -2231,148 +2231,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
     return x;
 }
 
-
-/**
-Concatenate two arrays into a new array
-
----
-void main()
-{
-    int[] x = [10, 20, 30];
-    int[] y = [40, 50];
-    int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]);
-}
----
-
-Params:
-    ti = type that the two arrays share
-    x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
-    y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
-Returns:
-    resulting concatenated array, with `.length` equal to new element length despite `byte` type
-*/
-extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak
-out (result)
-{
-    auto tinext = unqualify(ti.next);
-    auto sizeelem = tinext.tsize;              // array element size
-    debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr);
-    assert(result.length == x.length + y.length);
-
-    // If a postblit is involved, the contents of result might rightly differ
-    // from the bitwise concatenation of x and y.
-    if (!hasPostblit(tinext))
-    {
-        for (size_t i = 0; i < x.length * sizeelem; i++)
-            assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
-        for (size_t i = 0; i < y.length * sizeelem; i++)
-            assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);
-    }
-
-    size_t cap = GC.sizeOf(result.ptr);
-    assert(!cap || cap > result.length * sizeelem);
-}
-do
-{
-    import core.stdc.string;
-    version (none)
-    {
-        /* Cannot use this optimization because:
-         *  char[] a, b;
-         *  char c = 'a';
-         *  b = a ~ c;
-         *  c = 'b';
-         * will change the contents of b.
-         */
-        if (!y.length)
-            return x;
-        if (!x.length)
-            return y;
-    }
-
-    auto tinext = unqualify(ti.next);
-    auto sizeelem = tinext.tsize;              // array element size
-    debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
-    size_t xlen = x.length * sizeelem;
-    size_t ylen = y.length * sizeelem;
-    size_t len  = xlen + ylen;
-
-    if (!len)
-        return null;
-
-    auto info = __arrayAlloc(len, ti, tinext);
-    byte* p = cast(byte*)__arrayStart(info);
-    p[len] = 0; // guessing this is to optimize for null-terminated arrays?
-    memcpy(p, x.ptr, xlen);
-    memcpy(p + xlen, y.ptr, ylen);
-    // do postblit processing
-    __doPostblit(p, xlen + ylen, tinext);
-
-    auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-    __setArrayAllocLength(info, len, isshared, tinext);
-    return p[0 .. x.length + y.length];
-}
-
-
-/**
-Concatenate multiple arrays at once
-
-This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance.
-
-```
-void main()
-{
-    int[] a, b, c;
-    int[] res = a ~ b ~ c;
-    // _d_arraycatnTX(typeid(int[]),
-    //    [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]);
-}
-```
-
-Params:
-    ti = type of arrays to concatenate and resulting array
-    arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same
-
-Returns:
-    newly created concatenated array, `.length` equal to the total element length despite `void` type
-*/
-extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
-{
-    import core.stdc.string;
-
-    size_t length;
-    auto tinext = unqualify(ti.next);
-    auto size = tinext.tsize;   // array element size
-
-    foreach (b; arrs)
-        length += b.length;
-
-    if (!length)
-        return null;
-
-    auto allocsize = length * size;
-    auto info = __arrayAlloc(allocsize, ti, tinext);
-    auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-    __setArrayAllocLength(info, allocsize, isshared, tinext);
-    void *a = __arrayStart (info);
-
-    size_t j = 0;
-    foreach (b; arrs)
-    {
-        if (b.length)
-        {
-            memcpy(a + j, b.ptr, b.length * size);
-            j += b.length * size;
-        }
-    }
-
-    // do postblit processing
-    __doPostblit(a, j, tinext);
-
-    return a[0..length];
-}
-
-
 /**
 Allocate an array literal
 
index 45e0d51b7115c73745769184f6952e74d8d2969c..b97a5c5437b0dfac9e3ca496c6ea6ef77bdecef8 100644 (file)
@@ -15,6 +15,7 @@ module rt.profilegc;
 
 private:
 
+import core.stdc.errno;
 import core.stdc.stdio;
 import core.stdc.stdlib;
 import core.stdc.string;
@@ -151,7 +152,7 @@ shared static ~this()
     {
         qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp);
 
-        FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w");
+        FILE* fp = logfilename == "\0" ? stdout : fopen((logfilename).ptr, "w");
         if (fp)
         {
             fprintf(fp, "bytes allocated, allocations, type, function, file:line\n");
@@ -165,6 +166,12 @@ shared static ~this()
                 fclose(fp);
         }
         else
-            fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr);
+        {
+            const err = errno;
+            fprintf(stderr, "cannot write profilegc log file '%.*s' (errno=%d)",
+                cast(int) logfilename.length,
+                logfilename.ptr,
+                cast(int) err);
+        }
     }
 }
index 2b6bc3e619068e93a0e6d45931d1e0c0db7ec630..c6e61f78fd0925fe8cf49094432cde885a09d864 100644 (file)
@@ -1,4 +1,4 @@
-106038f2eaa70045bf25b29bb1c789304a6065f7
+8ab95ded5265379e74d507fdc252ff3d2305fc26
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
index 5ecb4f601a51e9573597da3c440f804deb43a35d..5c70960a711fc15c36c03f208c634a832c29ffd7 100644 (file)
@@ -577,19 +577,20 @@ Returns:
     and `T3` are different.
 */
 T1 clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
-if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val))
-    && (is(T2 : T1) && is(T3 : T1)))
-// cannot use :
-// `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1))
-// because of https://issues.dlang.org/show_bug.cgi?id=16235.
-// Once that is fixed, we can simply use the ternary in both the template constraint
-// and the template body
-in
 {
+    static assert(is(T2 : T1), "T2 of type '", T2.stringof
+            , "' must be implicitly convertible to type of T1 '"
+            , T1.stringof, "'");
+    static assert(is(T3 : T1), "T3 of type '", T3.stringof
+            , "' must be implicitly convertible to type of T1 '"
+            , T1.stringof, "'");
+
     assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
-}
-do
-{
+
+    // `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1))
+    // because of https://issues.dlang.org/show_bug.cgi?id=16235.
+    // Once that is fixed, we can simply use the ternary in both the template constraint
+    // and the template body
     if (val.lessThan(lower))
         return lower;
     else if (val.greaterThan(upper))
index 82360763adc3d969291abd7aef9f581668261c70..9f5a6ac7bc9fcc89f80fd4a31db238d8901a1164 100644 (file)
@@ -3632,18 +3632,18 @@ auto joiner(RoR, Separator)(RoR r, Separator sep)
 
 /// Ditto
 auto joiner(RoR)(RoR r)
-if (isInputRange!RoR && isInputRange!(ElementType!RoR))
+if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)))
 {
     static struct Result
     {
     private:
         RoR _items;
-        ElementType!RoR _current;
+        Unqual!(ElementType!RoR) _current;
         enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) &&
                                isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR);
         static if (isBidirectional)
         {
-            ElementType!RoR _currentBack;
+            Unqual!(ElementType!RoR) _currentBack;
             bool reachedFinalElement;
         }
 
@@ -4293,6 +4293,28 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
     assert([only(S(null))].joiner.front == S(null));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=22785
+@safe unittest
+{
+
+    import std.algorithm.iteration : joiner, map;
+    import std.array : array;
+
+    static immutable struct S
+    {
+        int value;
+    }
+
+    static immutable struct T
+    {
+        S[] arr;
+    }
+
+    auto range = [T([S(3)]), T([S(4), S(5)])];
+
+    assert(range.map!"a.arr".joiner.array == [S(3), S(4), S(5)]);
+}
+
 /++
 Implements the homonym function (also known as `accumulate`, $(D
 compress), `inject`, or `foldl`) present in various programming
index 15f7ca99e66ead2ae8cb5b8234c073075eadfbb6..ee318c88a0f5bbced2b9c97f46f7ed0ffe833273 100644 (file)
@@ -119,12 +119,9 @@ template all(alias pred = "a")
     Performs (at most) $(BIGOH range.length) evaluations of `pred`.
      +/
     bool all(Range)(Range range)
-    if (isInputRange!Range)
+    if (isInputRange!Range &&
+        (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front)))))
     {
-        static assert(is(typeof(unaryFun!pred(range.front))),
-                "`" ~ (isSomeString!(typeof(pred))
-                    ? pred.stringof[1..$-1] : pred.stringof)
-                ~ "` isn't a unary predicate function for range.front");
         import std.functional : not;
 
         return find!(not!(unaryFun!pred))(range).empty;
@@ -172,7 +169,8 @@ template any(alias pred = "a")
     Performs (at most) $(BIGOH range.length) evaluations of `pred`.
      +/
     bool any(Range)(Range range)
-    if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+    if (isInputRange!Range &&
+        (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front)))))
     {
         return !find!pred(range).empty;
     }
@@ -1294,17 +1292,6 @@ if (isInputRange!R &&
 
 private enum bool hasConstEmptyMember(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool);
 
-// Rebindable doesn't work with structs
-// see: https://github.com/dlang/phobos/pull/6136
-private template RebindableOrUnqual(T)
-{
-    import std.typecons : Rebindable;
-    static if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
-        alias RebindableOrUnqual = Rebindable!T;
-    else
-        alias RebindableOrUnqual = Unqual!T;
-}
-
 /**
 Iterates the passed range and selects the extreme element with `less`.
 If the extreme element occurs multiple time, the first occurrence will be
@@ -1313,8 +1300,8 @@ returned.
 Params:
     map = custom accessor for the comparison key
     selector = custom mapping for the extrema selection
-    seed = custom seed to use as initial element
     r = Range from which the extreme value will be selected
+    seedElement = custom seed to use as initial element
 
 Returns:
     The extreme value according to `map` and `selector` of the passed-in values.
@@ -1328,10 +1315,19 @@ in
 }
 do
 {
+    import std.typecons : Rebindable;
+
     alias Element = ElementType!Range;
-    RebindableOrUnqual!Element seed = r.front;
+    Rebindable!Element seed = r.front;
     r.popFront();
-    return extremum!(map, selector)(r, seed);
+    static if (is(Rebindable!Element == T[], T))
+    {
+        return extremum!(map, selector)(r, seed);
+    }
+    else
+    {
+        return extremum!(map, selector)(r, seed.get);
+    }
 }
 
 private auto extremum(alias map, alias selector = "a < b", Range,
@@ -1341,13 +1337,14 @@ if (isInputRange!Range && !isInfinite!Range &&
     !is(CommonType!(ElementType!Range, RangeElementType) == void) &&
      is(typeof(unaryFun!map(ElementType!(Range).init))))
 {
+    import std.typecons : Rebindable;
+
     alias mapFun = unaryFun!map;
     alias selectorFun = binaryFun!selector;
 
     alias Element = ElementType!Range;
     alias CommonElement = CommonType!(Element, RangeElementType);
-    RebindableOrUnqual!CommonElement extremeElement = seedElement;
-
+    Rebindable!CommonElement extremeElement = seedElement;
 
     // if we only have one statement in the loop, it can be optimized a lot better
     static if (__traits(isSame, map, a => a))
@@ -1408,7 +1405,15 @@ if (isInputRange!Range && !isInfinite!Range &&
             }
         }
     }
-    return extremeElement;
+    // Rebindable is an alias to T for arrays
+    static if (is(typeof(extremeElement) == T[], T))
+    {
+        return extremeElement;
+    }
+    else
+    {
+        return extremeElement.get;
+    }
 }
 
 private auto extremum(alias selector = "a < b", Range)(Range r)
@@ -1493,6 +1498,10 @@ if (isInputRange!Range && !isInfinite!Range &&
         assert(d.extremum!`a > b` == 10);
         assert(d.extremum!(a => a, `a > b`) == 10);
     }
+
+    // compiletime
+    enum ctExtremum = iota(1, 5).extremum;
+    assert(ctExtremum == 1);
 }
 
 @nogc @safe nothrow pure unittest
@@ -1524,6 +1533,17 @@ if (isInputRange!Range && !isInfinite!Range &&
     assert(arr.extremum!"a.val".val == 0);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=22786
+@nogc @safe nothrow pure unittest
+{
+    struct S
+    {
+        immutable int value;
+    }
+
+    assert([S(5), S(6)].extremum!"a.value" == S(5));
+}
+
 // find
 /**
 Finds an individual element in an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
@@ -1552,7 +1572,7 @@ Complexity:
     `find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`.
     There are specializations that improve performance by taking
     advantage of $(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives)
-    or $(REF_ALTTEXT random access, isRandomAccess, std,range,primitives)
+    or $(REF_ALTTEXT random access, isRandomAccessRange, std,range,primitives)
     ranges (where possible).
 
 Params:
index 4584dcc92ddb24b721808475a071fe28c03f89f2..26528032ded1e9037cd5b6f463c0d3255d69ffeb 100644 (file)
@@ -2565,33 +2565,8 @@ E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to)
 if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
     is(Unqual!E : Unqual!R1))
 {
-    import std.algorithm.searching : find;
-    import std.range : dropOne;
-
-    static if (isInputRange!R1)
-    {
-        if (from.empty) return subject;
-        alias rSave = a => a.save;
-    }
-    else
-    {
-        alias rSave = a => a;
-    }
-
-    auto balance = find(subject, rSave(from));
-    if (balance.empty)
-        return subject;
-
-    auto app = appender!(E[])();
-    app.put(subject[0 .. subject.length - balance.length]);
-    app.put(rSave(to));
-    // replacing an element in an array is different to a range replacement
-    static if (is(Unqual!E : Unqual!R1))
-        replaceInto(app, balance.dropOne, from, to);
-    else
-        replaceInto(app, balance[from.length .. $], from, to);
-
-    return app.data;
+    size_t changed = 0;
+    return replace(subject, from, to, changed);
 }
 
 ///
@@ -2645,55 +2620,87 @@ if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)
 }
 
 /++
-    Replace occurrences of `from` with `to` in `subject` and output the result into
-    `sink`.
+    Replace occurrences of `from` with `to` in `subject` in a new array.
+    `changed` counts how many replacements took place.
 
     Params:
-        sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
         subject = the array to scan
         from = the item to replace
         to = the item to replace all instances of `from` with
+        changed = the number of replacements
 
-    See_Also:
-        $(REF substitute, std,algorithm,iteration) for a lazy replace.
+    Returns:
+        A new array without changing the contents of `subject`, or the original
+        array if no match is found.
  +/
-void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
-if (isOutputRange!(Sink, E) &&
-    ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
-    is(Unqual!E : Unqual!R1)))
+E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to, ref size_t changed)
+if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+    is(Unqual!E : Unqual!R1))
 {
     import std.algorithm.searching : find;
     import std.range : dropOne;
 
     static if (isInputRange!R1)
     {
-        if (from.empty)
-        {
-            sink.put(subject);
-            return;
-        }
+        if (from.empty) return subject;
         alias rSave = a => a.save;
     }
     else
     {
         alias rSave = a => a;
     }
-    for (;;)
-    {
-        auto balance = find(subject, rSave(from));
-        if (balance.empty)
-        {
-            sink.put(subject);
-            break;
-        }
-        sink.put(subject[0 .. subject.length - balance.length]);
-        sink.put(rSave(to));
-        // replacing an element in an array is different to a range replacement
-        static if (is(Unqual!E : Unqual!R1))
-            subject = balance.dropOne;
-        else
-            subject = balance[from.length .. $];
-    }
+
+    auto balance = find(subject, rSave(from));
+    if (balance.empty)
+        return subject;
+
+    auto app = appender!(E[])();
+    app.put(subject[0 .. subject.length - balance.length]);
+    app.put(rSave(to));
+    ++changed;
+    // replacing an element in an array is different to a range replacement
+    static if (is(Unqual!E : Unqual!R1))
+        replaceInto(app, balance.dropOne, from, to, changed);
+    else
+        replaceInto(app, balance[from.length .. $], from, to, changed);
+
+    return app.data;
+}
+
+///
+@safe unittest
+{
+    size_t changed = 0;
+    assert("Hello Wörld".replace("o Wö", "o Wo", changed) == "Hello World");
+    assert(changed == 1);
+
+    changed = 0;
+    assert("Hello Wörld".replace("l", "h", changed) == "Hehho Wörhd");
+    import std.stdio : writeln;
+    writeln(changed);
+    assert(changed == 3);
+}
+
+/++
+    Replace occurrences of `from` with `to` in `subject` and output the result into
+    `sink`.
+
+    Params:
+        sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+        subject = the array to scan
+        from = the item to replace
+        to = the item to replace all instances of `from` with
+
+    See_Also:
+        $(REF substitute, std,algorithm,iteration) for a lazy replace.
+ +/
+void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
+if (isOutputRange!(Sink, E) &&
+    ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+    is(Unqual!E : Unqual!R1)))
+{
+    size_t changed = 0;
+    replaceInto(sink, subject, from, to, changed);
 }
 
 ///
@@ -2783,6 +2790,72 @@ if (isOutputRange!(Sink, E) &&
     assert(sink2.data == "abö");
 }
 
+/++
+    Replace occurrences of `from` with `to` in `subject` and output the result into
+    `sink`. `changed` counts how many replacements took place.
+
+    Params:
+        sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+        subject = the array to scan
+        from = the item to replace
+        to = the item to replace all instances of `from` with
+        changed = the number of replacements
+ +/
+void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to, ref size_t changed)
+if (isOutputRange!(Sink, E) &&
+    ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+    is(Unqual!E : Unqual!R1)))
+{
+    import std.algorithm.searching : find;
+    import std.range : dropOne;
+
+    static if (isInputRange!R1)
+    {
+        if (from.empty)
+        {
+            sink.put(subject);
+            return;
+        }
+        alias rSave = a => a.save;
+    }
+    else
+    {
+        alias rSave = a => a;
+    }
+    for (;;)
+    {
+        auto balance = find(subject, rSave(from));
+        if (balance.empty)
+        {
+            sink.put(subject);
+            break;
+        }
+        sink.put(subject[0 .. subject.length - balance.length]);
+        sink.put(rSave(to));
+        ++changed;
+        // replacing an element in an array is different to a range replacement
+        static if (is(Unqual!E : Unqual!R1))
+            subject = balance.dropOne;
+        else
+            subject = balance[from.length .. $];
+    }
+}
+
+///
+@safe unittest
+{
+    auto arr = [1, 2, 3, 4, 5];
+    auto from = [2, 3];
+    auto to = [4, 6];
+    auto sink = appender!(int[])();
+
+    size_t changed = 0;
+    replaceInto(sink, arr, from, to, changed);
+
+    assert(sink.data == [1, 4, 6, 4, 5]);
+    assert(changed == 1);
+}
+
 /++
     Replaces elements from `array` with indices ranging from `from`
     (inclusive) to `to` (exclusive) with the range `stuff`.
index 7fe52ba55ec7b8b676cd55511d36f834724967e4..267f682b3e964f3146ecc59efea26531b3099ba1 100644 (file)
@@ -2270,7 +2270,9 @@ private
                     if (range.front.convertsTo!(Throwable))
                         throw range.front.get!(Throwable);
                     else if (range.front.convertsTo!(shared(Throwable)))
-                        throw range.front.get!(shared(Throwable));
+                        /* Note: a shared type can be caught without the shared qualifier
+                         * so throwing shared will be an error */
+                        throw cast() range.front.get!(shared(Throwable));
                     else
                         throw new PriorityMessageException(range.front.data);
                 }
index 11e3883e5ee408203f3fccf00a06dea021f386d8..4fdf13d486f107777d2db9145dae3e7344f6e6ba 100644 (file)
@@ -198,8 +198,10 @@ struct DList(T)
 
         this (BaseNode _base, T _payload)
         {
+            import std.algorithm.mutation : move;
+
             this._base = _base;
-            this._payload = _payload;
+            this._payload = move(_payload);
         }
 
         inout(BaseNode)* asBaseNode() inout @trusted
@@ -216,7 +218,9 @@ struct DList(T)
     //Construct as new PayNode, and returns it as a BaseNode.
     static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null)
     {
-        return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
+        import std.algorithm.mutation : move;
+
+        return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode();
     }
 
     void initialize() nothrow @safe pure
@@ -721,7 +725,9 @@ Complexity: $(BIGOH n)
      */
     bool linearRemoveElement(T value)
     {
-        auto n1 = findNodeByValue(_root, value);
+        import std.algorithm.mutation : move;
+
+        auto n1 = findNodeByValue(_root, move(value));
         if (n1)
         {
             auto n2 = n1._next._next;
@@ -1118,3 +1124,27 @@ private:
     a.insertFront(iota(0, 5)); // can insert range with non-ref front
     assert(a.front == 0 && a.back == 4);
 }
+
+// https://issues.dlang.org/show_bug.cgi?id=22147
+@safe unittest
+{
+    import std.algorithm.mutation : move;
+
+    static struct Item
+    {
+        @disable this(this);
+
+        int x;
+    }
+
+    auto list = DList!Item();
+    list.insertFront(Item(1));
+    assert(list[].walkLength == 1);
+    assert(list.front.x == 1);
+    auto item = list.moveFront;
+    item.x = 2;
+    list.front = move(item);
+    assert(list.front.x == 2);
+    list.removeFront();
+    assert(list[].walkLength == 0);
+}
index 8c397849f4327a74aef4335d5634e7270bb95024..a23746a236b6187afac51c181721094a75c97d8d 100644 (file)
@@ -695,25 +695,15 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
     import std.conv : to;
     import std.traits : hasMember;
     import std.typecons : Ternary;
+    import core.thread.types : isStackGrowingDown;
 
     static assert(minAlign.isGoodStaticAlignment);
     static assert(size >= minAlign);
 
-    version (X86) enum growDownwards = Yes.growDownwards;
-    else version (X86_64) enum growDownwards = Yes.growDownwards;
-    else version (ARM) enum growDownwards = Yes.growDownwards;
-    else version (AArch64) enum growDownwards = Yes.growDownwards;
-    else version (HPPA) enum growDownwards = No.growDownwards;
-    else version (PPC) enum growDownwards = Yes.growDownwards;
-    else version (PPC64) enum growDownwards = Yes.growDownwards;
-    else version (RISCV32) enum growDownwards = Yes.growDownwards;
-    else version (RISCV64) enum growDownwards = Yes.growDownwards;
-    else version (MIPS32) enum growDownwards = Yes.growDownwards;
-    else version (MIPS64) enum growDownwards = Yes.growDownwards;
-    else version (SPARC) enum growDownwards = Yes.growDownwards;
-    else version (SPARC64) enum growDownwards = Yes.growDownwards;
-    else version (SystemZ) enum growDownwards = Yes.growDownwards;
-    else static assert(0, "Dunno how the stack grows on this architecture.");
+    static if (isStackGrowingDown)
+        enum growDownwards = Yes.growDownwards;
+    else
+        enum growDownwards = No.growDownwards;
 
     @disable this(this);
 
index b7bd3fc91cf971f761048a8b913dbbf7ae76dff2..5b8925d5eb4e40cd02f13e3628180afc69058e29 100644 (file)
@@ -310,6 +310,8 @@ Params:
 Returns: Untyped array of bytes _read.
 
 Throws: $(LREF FileException) on error.
+
+See_Also: $(REF readText, std,file) for reading and validating a text file.
  */
 
 void[] read(R)(R name, size_t upTo = size_t.max)
@@ -497,6 +499,8 @@ version (linux) @safe unittest
 
     Throws: $(LREF FileException) if there is an error reading the file,
             $(REF UTFException, std, utf) on UTF decoding error.
+
+    See_Also: $(REF read, std,file) for reading a binary file.
 +/
 S readText(S = string, R)(auto ref R name)
 if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R)))
index 088e77fd66ea2c94bd2e97ceee85936da6cd512c..219af7143731f6a4e5720a97a4ad8963ff52b0a5 100644 (file)
@@ -1,7 +1,10 @@
 // Written in the D programming language.
 
 /**
-JavaScript Object Notation
+Implements functionality to read and write JavaScript Object Notation values.
+
+JavaScript Object Notation is a lightweight data interchange format commonly used in web services and configuration files.
+It's easy for humans to read and write, and it's easy for machines to parse and generate.
 
 Copyright: Copyright Jeremie Pelletier 2008 - 2009.
 License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -602,6 +605,45 @@ struct JSONValue
         assert(j.type == JSONType.object);
     }
 
+    /**
+     * An enum value that can be used to obtain a `JSONValue` representing
+     * an empty JSON object.
+     */
+    enum emptyObject = JSONValue(string[string].init);
+    ///
+    @system unittest
+    {
+        JSONValue obj1 = JSONValue.emptyObject;
+        assert(obj1.type == JSONType.object);
+        obj1.object["a"] = JSONValue(1);
+        assert(obj1.object["a"] == JSONValue(1));
+
+        JSONValue obj2 = JSONValue.emptyObject;
+        assert("a" !in obj2.object);
+        obj2.object["b"] = JSONValue(5);
+        assert(obj1 != obj2);
+    }
+
+    /**
+     * An enum value that can be used to obtain a `JSONValue` representing
+     * an empty JSON array.
+     */
+    enum emptyArray = JSONValue(JSONValue[].init);
+    ///
+    @system unittest
+    {
+        JSONValue arr1 = JSONValue.emptyArray;
+        assert(arr1.type == JSONType.array);
+        assert(arr1.array.length == 0);
+        arr1.array ~= JSONValue("Hello");
+        assert(arr1.array.length == 1);
+        assert(arr1.array[0] == JSONValue("Hello"));
+
+        JSONValue arr2 = JSONValue.emptyArray;
+        assert(arr2.array.length == 0);
+        assert(arr1 != arr2);
+    }
+
     void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue))
     {
         assign(arg);
index 42a34b9bd559791c87f05d30f7eaa48128368953..2fcbf943122de4d5e3c4ada09d6dd160b19c3362 100644 (file)
@@ -23,7 +23,7 @@ SMTP) )
 )
 
 Note:
-You may need to link to the $(B curl) library, e.g. by adding $(D "libs": ["curl"])
+You may need to link with the $(B curl) library, e.g. by adding $(D "libs": ["curl"])
 to your $(B dub.json) file if you are using $(LINK2 http://code.dlang.org, DUB).
 
 Windows x86 note:
@@ -32,20 +32,19 @@ $(LINK2 https://downloads.dlang.org/other/index.html, download archive page).
 
 This module is not available for iOS, tvOS or watchOS.
 
-Compared to using libcurl directly this module allows simpler client code for
+Compared to using libcurl directly, this module allows simpler client code for
 common uses, requires no unsafe operations, and integrates better with the rest
-of the language. Futhermore it provides $(MREF_ALTTEXT range, std,range)
+of the language. Furthermore it provides $(MREF_ALTTEXT range, std,range)
 access to protocols supported by libcurl both synchronously and asynchronously.
 
 A high level and a low level API are available. The high level API is built
 entirely on top of the low level one.
 
 The high level API is for commonly used functionality such as HTTP/FTP get. The
-$(LREF byLineAsync) and $(LREF byChunkAsync) provides asynchronous
-$(MREF_ALTTEXT range, std,range) that performs the request in another
-thread while handling a line/chunk in the current thread.
+$(LREF byLineAsync) and $(LREF byChunkAsync) functions asynchronously
+perform the request given, outputting the fetched content into a $(MREF_ALTTEXT range, std,range).
 
-The low level API allows for streaming and other advanced features.
+The low level API allows for streaming, setting request headers and cookies, and other advanced features.
 
 $(BOOKTABLE Cheat Sheet,
 $(TR $(TH Function Name) $(TH Description)
@@ -79,18 +78,18 @@ byChunk("dlang.org", 10)) returns a range of ubyte[10] containing the
 dlang.org web page.)
 )
 $(TR $(TDNW $(LREF byLineAsync)) $(TD $(D
-byLineAsync("dlang.org")) returns a range of char[] containing the dlang.org web
- page asynchronously.)
+byLineAsync("dlang.org")) asynchronously returns a range of char[] containing the dlang.org web
+ page.)
 )
 $(TR $(TDNW $(LREF byChunkAsync)) $(TD $(D
-byChunkAsync("dlang.org", 10)) returns a range of ubyte[10] containing the
-dlang.org web page asynchronously.)
+byChunkAsync("dlang.org", 10)) asynchronously returns a range of ubyte[10] containing the
+dlang.org web page.)
 )
 $(LEADINGROW Low level
 )
-$(TR $(TDNW $(LREF HTTP)) $(TD `HTTP` struct for advanced usage))
-$(TR $(TDNW $(LREF FTP)) $(TD `FTP` struct for advanced usage))
-$(TR $(TDNW $(LREF SMTP)) $(TD `SMTP` struct for advanced usage))
+$(TR $(TDNW $(LREF HTTP)) $(TD Struct for advanced HTTP usage))
+$(TR $(TDNW $(LREF FTP)) $(TD Struct for advanced FTP usage))
+$(TR $(TDNW $(LREF SMTP)) $(TD Struct for advanced SMTP usage))
 )
 
 
@@ -135,10 +134,10 @@ http.perform();
 First, an instance of the reference-counted HTTP struct is created. Then the
 custom delegates are set. These will be called whenever the HTTP instance
 receives a header and a data buffer, respectively. In this simple example, the
-headers are written to stdout and the data is ignored. If the request should be
+headers are written to stdout and the data is ignored. If the request is
 stopped before it has finished then return something less than data.length from
 the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more
-information. Finally the HTTP request is effected by calling perform(), which is
+information. Finally, the HTTP request is performed by calling perform(), which is
 synchronous.
 
 Source: $(PHOBOSSRC std/net/curl.d)
@@ -147,8 +146,8 @@ Copyright: Copyright Jonas Drewsen 2011-2012
 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
 Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao.
 
-Credits: The functionally is based on $(HTTP curl.haxx.se/libcurl, libcurl).
-         LibCurl is licensed under an MIT/X derivative license.
+Credits: The functionality is based on $(HTTP curl.haxx.se/libcurl, libcurl).
+         libcurl is licensed under an MIT/X derivative license.
 */
 /*
          Copyright Jonas Drewsen 2011 - 2012.
index 4b5a7efe07f2e742eeda26ac52de1915c01af8d2..63d60d1d77ff1185f32fdb97adcbf88d64229e98 100644 (file)
@@ -1758,7 +1758,6 @@ immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...)
 if (isSomeChar!C)
 {
     import std.array : array;
-    import std.exception : assumeUnique;
 
     const(C)[] chained;
     foreach (path; paths)
@@ -1770,7 +1769,7 @@ if (isSomeChar!C)
     }
     auto result = asNormalizedPath(chained);
     // .array returns a copy, so it is unique
-    return () @trusted { return assumeUnique(result.array); } ();
+    return result.array;
 }
 
 ///
index 93be764abd182222fb9fad7378ea65b889de0f03..066ed1713e26322d195c96156a39af7b1e20218b 100644 (file)
@@ -812,7 +812,7 @@ Parameters for the generator.
 
     // Bitmasks used in the 'twist' part of the algorithm
     private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1,
-                          upperMask = (~lowerMask) & this.max;
+                          upperMask = (~lowerMask) & max;
 
     /*
        Collection of all state variables
@@ -905,17 +905,17 @@ Parameters for the generator.
     private static void seedImpl(UIntType value, ref State mtState) @nogc
     {
         mtState.data[$ - 1] = value;
-        static if (this.max != UIntType.max)
+        static if (max != UIntType.max)
         {
-            mtState.data[$ - 1] &= this.max;
+            mtState.data[$ - 1] &= max;
         }
 
         foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1])
         {
             e = f * (mtState.data[i + 1] ^ (mtState.data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1));
-            static if (this.max != UIntType.max)
+            static if (max != UIntType.max)
             {
-                e &= this.max;
+                e &= max;
             }
         }
 
index 888ac1a5d86c97438b63ff689ba22266b6a68c3c..7a724b0f718f93d6ced725bd57dd209caeb9713a 100644 (file)
@@ -235,7 +235,7 @@ public import std.range.primitives;
 public import std.typecons : Flag, Yes, No;
 
 import std.internal.attributes : betterC;
-import std.meta : allSatisfy, anySatisfy, staticMap;
+import std.meta : aliasSeqOf, allSatisfy, anySatisfy, staticMap;
 import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral,
     isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf;
 
@@ -313,12 +313,16 @@ if (isBidirectionalRange!(Unqual!Range))
             {
                 @property void front(ElementType!R val)
                 {
-                    source.back = val;
+                    import std.algorithm.mutation : move;
+
+                    source.back = move(val);
                 }
 
                 @property void back(ElementType!R val)
                 {
-                    source.front = val;
+                    import std.algorithm.mutation : move;
+
+                    source.front = move(val);
                 }
             }
 
@@ -330,7 +334,9 @@ if (isBidirectionalRange!(Unqual!Range))
                 {
                     void opIndexAssign(ElementType!R val, size_t n)
                     {
-                        source[retroIndex(n)] = val;
+                        import std.algorithm.mutation : move;
+
+                        source[retroIndex(n)] = move(val);
                     }
                 }
 
@@ -474,6 +480,19 @@ pure @safe nothrow @nogc unittest
     foreach (x; data.retro) {}
 }
 
+pure @safe nothrow unittest
+{
+    import std.algorithm.comparison : equal;
+
+    static struct S {
+        int v;
+        @disable this(this);
+    }
+
+    immutable foo = [S(1), S(2), S(3)];
+    auto r = retro(foo);
+    assert(equal(r, [S(3), S(2), S(1)]));
+}
 
 /**
 Iterates range `r` with stride `n`. If the range is a
@@ -585,7 +604,9 @@ do
             {
                 @property void front(ElementType!R val)
                 {
-                    source.front = val;
+                    import std.algorithm.mutation : move;
+
+                    source.front = move(val);
                 }
             }
 
@@ -864,6 +885,20 @@ pure @safe nothrow unittest
     assert(equal(s, [1L, 4L, 7L]));
 }
 
+pure @safe nothrow unittest
+{
+    import std.algorithm.comparison : equal;
+
+    static struct S {
+        int v;
+        @disable this(this);
+    }
+
+    immutable foo = [S(1), S(2), S(3), S(4), S(5)];
+    auto r = stride(foo, 3);
+    assert(equal(r, [S(1), S(4)]));
+}
+
 /**
 Spans multiple ranges in sequence. The function `chain` takes any
 number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
@@ -872,6 +907,11 @@ result is a range that offers the `front`, `popFront`, and $(D
 empty) primitives. If all input ranges offer random access and $(D
 length), `Chain` offers them as well.
 
+Note that repeated random access of the resulting range is likely
+to perform somewhat badly since lengths of the ranges in the chain have to be
+added up for each random access operation. Random access to elements of
+the first remaining range is still efficient.
+
 If only one range is offered to `Chain` or `chain`, the $(D
 Chain) type exits the picture by aliasing itself directly to that
 range's type.
@@ -907,7 +947,12 @@ if (Ranges.length > 0 &&
                 enum sameET = is(.ElementType!A == RvalueElementType);
             }
 
-            enum bool allSameType = allSatisfy!(sameET, R);
+            enum bool allSameType = allSatisfy!(sameET, R),
+                bidirectional = allSatisfy!(isBidirectionalRange, R),
+                mobileElements = allSatisfy!(hasMobileElements, R),
+                assignableElements = allSameType
+                    && allSatisfy!(hasAssignableElements, R);
+
             alias ElementType = RvalueElementType;
 
             static if (allSameType && allSatisfy!(hasLvalueElements, R))
@@ -925,17 +970,44 @@ if (Ranges.length > 0 &&
                 }
             }
 
-            // This is the entire state
             R source;
-            // TODO: use a vtable (or more) instead of linear iteration
+            size_t frontIndex;
+            // Always points to index one past the last non-empty range,
+            // because otherwise decrementing while pointing to first range
+            // would overflow to size_t.max.
+            static if (bidirectional) size_t backIndex;
+            else enum backIndex = source.length;
 
         public:
             this(R input)
             {
-                // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209
-                static foreach (i, v; input)
+                frontIndex = source.length;
+                static if (bidirectional) backIndex = 0;
+
+                foreach (i, ref v; input) source[i] = v;
+
+                // We do this separately to avoid invoking `empty` needlessly.
+                // While not recommended, a range may depend on side effects of
+                // `empty` call.
+                foreach (i, ref v; input) if (!v.empty)
                 {
-                    source[i] = v;
+                    frontIndex = i;
+                    static if (bidirectional) backIndex = i+1;
+                    break;
+                }
+
+                // backIndex is already set in the first loop to
+                // as frontIndex+1, so we'll use that if we don't find a
+                // non-empty range here.
+                static if (bidirectional)
+                    static foreach_reverse (i; 1 .. R.length + 1)
+                {
+                    if (i <= frontIndex + 1) return;
+                    if (!input[i-1].empty)
+                    {
+                        backIndex = i;
+                        return;
+                    }
                 }
             }
 
@@ -948,120 +1020,209 @@ if (Ranges.length > 0 &&
             }
             else
             {
-                @property bool empty()
+                @property bool empty() => frontIndex >= backIndex;
+            }
+
+            static if (allSatisfy!(isForwardRange, R))
+            {
+                @property auto save()
                 {
-                    foreach (i, Unused; R)
+                    auto saveI(size_t i)() => source[i].save;
+
+                    // TODO: this has the constructor needlessly refind
+                    // frontIndex and backIndex. It'd be better to just copy
+                    // those from `.this`.
+                    auto saveResult =
+                        Result(staticMap!(saveI, aliasSeqOf!(R.length.iota)));
+
+                    return saveResult;
+                }
+            }
+
+            void popFront()
+            {
+                sw1: switch (frontIndex)
+                {
+                    static foreach (i; 0 .. R.length)
                     {
-                        if (!source[i].empty) return false;
+                    case i:
+                        source[i].popFront();
+                        break sw1;
                     }
-                    return true;
+
+                case R.length:
+                    assert(0, "Attempt to `popFront` of empty `chain` range");
+
+                default:
+                    assert(0, "Internal library error. Please report it.");
                 }
-            }
 
-            static if (allSatisfy!(isForwardRange, R))
-                @property auto save()
+                sw2: switch (frontIndex)
                 {
-                    auto saveSource(size_t len)()
+                    static foreach (i; 0 .. R.length)
                     {
-                        import std.typecons : tuple;
-                        static assert(len > 0);
-                        static if (len == 1)
-                        {
-                            return tuple(source[0].save);
-                        }
-                        else
+                    case i:
+                        if (source[i].empty)
                         {
-                            return saveSource!(len - 1)() ~
-                                tuple(source[len - 1].save);
+                            frontIndex++;
+                            goto case;
                         }
+                        else break sw2;
                     }
-                    return Result(saveSource!(R.length).expand);
-                }
 
-            void popFront()
-            {
-                foreach (i, Unused; R)
-                {
-                    if (source[i].empty) continue;
-                    source[i].popFront();
-                    return;
+                // Only possible to reach from goto of previous case.
+                case R.length:
+                    break;
+
+                default:
+                    assert(0, "Internal library error. Please report it.");
                 }
-                assert(false, "Attempt to `popFront` of empty `chain` range");
             }
 
             @property auto ref front()
             {
-                foreach (i, Unused; R)
+                switch (frontIndex)
                 {
-                    if (source[i].empty) continue;
-                    return fixRef(source[i].front);
+                    static foreach (i; 0 .. R.length)
+                    {
+                    case i:
+                        return fixRef(source[i].front);
+                    }
+
+                case R.length:
+                    assert(0, "Attempt to get `front` of empty `chain` range");
+
+                default:
+                    assert(0, "Internal library error. Please report it.");
                 }
-                assert(false, "Attempt to get `front` of empty `chain` range");
             }
 
-            static if (allSameType && allSatisfy!(hasAssignableElements, R))
+            static if (assignableElements)
             {
                 // @@@BUG@@@
                 //@property void front(T)(T v) if (is(T : RvalueElementType))
 
                 @property void front(RvalueElementType v)
                 {
-                    foreach (i, Unused; R)
+                    import std.algorithm.mutation : move;
+
+                    sw: switch (frontIndex)
                     {
-                        if (source[i].empty) continue;
-                        source[i].front = v;
-                        return;
+                        static foreach (i; 0 .. R.length)
+                        {
+                        case i:
+                            source[i].front = move(v);
+                            break sw;
+                        }
+
+                    case R.length:
+                        assert(0, "Attempt to set `front` of empty `chain` range");
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
-                    assert(false, "Attempt to set `front` of empty `chain` range");
                 }
             }
 
-            static if (allSatisfy!(hasMobileElements, R))
+            static if (mobileElements)
             {
                 RvalueElementType moveFront()
                 {
-                    foreach (i, Unused; R)
+                    switch (frontIndex)
                     {
-                        if (source[i].empty) continue;
-                        return source[i].moveFront();
+                        static foreach (i; 0 .. R.length)
+                        {
+                        case i:
+                            return source[i].moveFront();
+                        }
+
+                    case R.length:
+                        assert(0, "Attempt to `moveFront` of empty `chain` range");
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
-                    assert(false, "Attempt to `moveFront` of empty `chain` range");
                 }
             }
 
-            static if (allSatisfy!(isBidirectionalRange, R))
+            static if (bidirectional)
             {
                 @property auto ref back()
                 {
-                    foreach_reverse (i, Unused; R)
+                    switch (backIndex)
                     {
-                        if (source[i].empty) continue;
-                        return fixRef(source[i].back);
+                        static foreach_reverse (i; 1 .. R.length + 1)
+                        {
+                        case i:
+                            return fixRef(source[i-1].back);
+                        }
+
+                    case 0:
+                        assert(0, "Attempt to get `back` of empty `chain` range");
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
-                    assert(false, "Attempt to get `back` of empty `chain` range");
                 }
 
                 void popBack()
                 {
-                    foreach_reverse (i, Unused; R)
+                    sw1: switch (backIndex)
                     {
-                        if (source[i].empty) continue;
-                        source[i].popBack();
-                        return;
+                        static foreach_reverse (i; 1 .. R.length + 1)
+                        {
+                        case i:
+                            source[i-1].popBack();
+                            break sw1;
+                        }
+
+                    case 0:
+                        assert(0, "Attempt to `popFront` of empty `chain` range");
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
+                    }
+
+                    sw2: switch (backIndex)
+                    {
+                        static foreach_reverse (i; 1 .. R.length + 1)
+                        {
+                        case i:
+                            if (source[i-1].empty)
+                            {
+                                backIndex--;
+                                goto case;
+                            }
+                            else break sw2;
+                        }
+
+                    // Only possible to reach from goto of previous case.
+                    case 0:
+                        break;
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
-                    assert(false, "Attempt to `popBack` of empty `chain` range");
                 }
 
-                static if (allSatisfy!(hasMobileElements, R))
+                static if (mobileElements)
                 {
                     RvalueElementType moveBack()
                     {
-                        foreach_reverse (i, Unused; R)
+                        switch (backIndex)
                         {
-                            if (source[i].empty) continue;
-                            return source[i].moveBack();
+                            static foreach_reverse (i; 1 .. R.length + 1)
+                            {
+                            case i:
+                                return source[i-1].moveBack();
+                            }
+
+                        case 0:
+                            assert(0, "Attempt to `moveBack` of empty `chain` range");
+
+                        default:
+                            assert(0, "Internal library error. Please report it.");
                         }
-                        assert(false, "Attempt to `moveBack` of empty `chain` range");
                     }
                 }
 
@@ -1069,13 +1230,23 @@ if (Ranges.length > 0 &&
                 {
                     @property void back(RvalueElementType v)
                     {
-                        foreach_reverse (i, Unused; R)
+                        import std.algorithm.mutation : move;
+
+                        sw: switch (backIndex)
                         {
-                            if (source[i].empty) continue;
-                            source[i].back = v;
-                            return;
+                            static foreach_reverse (i; 1 .. R.length + 1)
+                            {
+                            case i:
+                                source[i-1].back = move(v);
+                                break sw;
+                            }
+
+                        case 0:
+                            assert(0, "Attempt to set `back` of empty `chain` range");
+
+                        default:
+                            assert(0, "Internal library error. Please report it.");
                         }
-                        assert(false, "Attempt to set `back` of empty `chain` range");
                     }
                 }
             }
@@ -1084,11 +1255,24 @@ if (Ranges.length > 0 &&
             {
                 @property size_t length()
                 {
-                    size_t result;
-                    foreach (i, Unused; R)
+                    size_t result = 0;
+                    sw: switch (frontIndex)
                     {
-                        result += source[i].length;
+                        static foreach (i; 0 .. R.length)
+                        {
+                        case i:
+                            result += source[i].length;
+                            if (backIndex == i+1) break sw;
+                            else goto case;
+                        }
+
+                    case R.length:
+                        break;
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
+
                     return result;
                 }
 
@@ -1099,64 +1283,93 @@ if (Ranges.length > 0 &&
             {
                 auto ref opIndex(size_t index)
                 {
-                    foreach (i, Range; R)
+                    switch (frontIndex)
                     {
-                        static if (isInfinite!(Range))
-                        {
-                            return source[i][index];
-                        }
-                        else
+                        static foreach (i; 0 .. R.length)
                         {
-                            immutable length = source[i].length;
-                            if (index < length) return fixRef(source[i][index]);
-                            index -= length;
+                        case i:
+                            static if (!isInfinite!(R[i]))
+                            {
+                                immutable length = source[i].length;
+                                if (index >= length)
+                                {
+                                    index -= length;
+                                    goto case;
+                                }
+                            }
+
+                            return fixRef(source[i][index]);
                         }
+
+                    case R.length:
+                        assert(0, "Attempt to access out-of-bounds index of `chain` range");
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
-                    assert(false, "Attempt to access out-of-bounds index of `chain` range");
                 }
 
-                static if (allSatisfy!(hasMobileElements, R))
+                static if (mobileElements)
                 {
                     RvalueElementType moveAt(size_t index)
                     {
-                        foreach (i, Range; R)
+                        switch (frontIndex)
                         {
-                            static if (isInfinite!(Range))
+                            static foreach (i; 0 .. R.length)
                             {
+                            case i:
+                                static if (!isInfinite!(R[i]))
+                                {
+                                    immutable length = source[i].length;
+                                    if (index >= length)
+                                    {
+                                        index -= length;
+                                        goto case;
+                                    }
+                                }
+
                                 return source[i].moveAt(index);
                             }
-                            else
-                            {
-                                immutable length = source[i].length;
-                                if (index < length) return source[i].moveAt(index);
-                                index -= length;
-                            }
+
+                        case R.length:
+                            assert(0, "Attempt to move out-of-bounds index of `chain` range");
+
+                        default:
+                            assert(0, "Internal library error. Please report it.");
                         }
-                        assert(false, "Attempt to move out-of-bounds index of `chain` range");
                     }
                 }
 
                 static if (allSameType && allSatisfy!(hasAssignableElements, R))
                     void opIndexAssign(ElementType v, size_t index)
                     {
-                        foreach (i, Range; R)
+                        import std.algorithm.mutation : move;
+
+                        sw: switch (frontIndex)
                         {
-                            static if (isInfinite!(Range))
+                            static foreach (i; 0 .. R.length)
                             {
-                                source[i][index] = v;
-                            }
-                            else
-                            {
-                                immutable length = source[i].length;
-                                if (index < length)
+                            case i:
+                                static if (!isInfinite!(R[i]))
                                 {
-                                    source[i][index] = v;
-                                    return;
+                                    immutable length = source[i].length;
+                                    if (index >= length)
+                                    {
+                                        index -= length;
+                                        goto case;
+                                    }
                                 }
-                                index -= length;
+
+                                source[i][index] = move(v);
+                                break sw;
                             }
+
+                        case R.length:
+                            assert(0, "Attempt to write out-of-bounds index of `chain` range");
+
+                        default:
+                            assert(0, "Internal library error. Please report it.");
                         }
-                        assert(false, "Attempt to write out-of-bounds index of `chain` range");
                     }
             }
 
@@ -1164,40 +1377,74 @@ if (Ranges.length > 0 &&
                 auto opSlice(size_t begin, size_t end) return scope
                 {
                     auto result = this;
-                    foreach (i, Unused; R)
+
+                    sw: switch (frontIndex)
                     {
-                        immutable len = result.source[i].length;
-                        if (len < begin)
-                        {
-                            result.source[i] = result.source[i]
-                                [len .. len];
-                            begin -= len;
-                        }
-                        else
+                        static foreach (i; 0 .. R.length)
                         {
-                            result.source[i] = result.source[i]
-                                [begin .. len];
-                            break;
+                        case i:
+                            immutable len = result.source[i].length;
+                            if (len <= begin)
+                            {
+                                result.source[i] = result.source[i]
+                                    [len .. len];
+                                begin -= len;
+                                result.frontIndex++;
+                                goto case;
+                            }
+                            else
+                            {
+                                result.source[i] = result.source[i]
+                                    [begin .. len];
+                                break sw;
+                            }
                         }
+
+                    case R.length:
+                        assert(begin == 0,
+                            "Attempt to access out-of-bounds slice of `chain` range");
+                        break;
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
-                    auto cut = length;
-                    cut = cut <= end ? 0 : cut - end;
-                    foreach_reverse (i, Unused; R)
+
+                    // Overflow intentional if end index too big.
+                    // This will trigger the bounds check failure below.
+                    auto cut = length - end;
+
+                    sw2: switch (backIndex)
                     {
-                        immutable len = result.source[i].length;
-                        if (cut > len)
+                        static foreach_reverse (i; 1 .. R.length + 1)
                         {
-                            result.source[i] = result.source[i]
-                                [0 .. 0];
-                            cut -= len;
-                        }
-                        else
-                        {
-                            result.source[i] = result.source[i]
-                                [0 .. len - cut];
-                            break;
+                        case i:
+                            immutable len = result.source[i-1].length;
+                            if (len <= cut)
+                            {
+                                result.source[i-1] = result.source[i-1]
+                                    [0 .. 0];
+                                cut -= len;
+                                result.backIndex--;
+                                goto case;
+                            }
+                            else
+                            {
+                                result.source[i-1] = result.source[i-1]
+                                    [0 .. len - cut];
+                                break sw2;
+                            }
                         }
+
+                    case 0:
+                        assert(cut == 0, end > length?
+                            "Attempt to access out-of-bounds slice of `chain` range":
+                            "Attempt to access negative length slice of `chain` range");
+                        break sw2;
+
+                    default:
+                        assert(0, "Internal library error. Please report it.");
                     }
+
                     return result;
                 }
         }
@@ -1293,6 +1540,10 @@ pure @safe nothrow unittest
         auto s = chain(arr1, arr2, arr3);
         assert(s[5] == 6);
         assert(equal(s, witness));
+        assert(s[4 .. 6].equal(arr2));
+        assert(s[2 .. 5].equal([3, 4, 5]));
+        assert(s[0 .. 0].empty);
+        assert(s[7 .. $].empty);
         assert(s[5] == 6);
     }
     {
@@ -1392,6 +1643,28 @@ pure @safe unittest
     assert(equal(r, "foobar"));
 }
 
+pure @safe nothrow @nogc unittest
+{
+    // support non-copyable items
+
+    static struct S {
+        int v;
+        @disable this(this);
+    }
+
+    S[2] s0, s1;
+    foreach (ref el; chain(s0[], s1[]))
+    {
+        int n = el.v;
+    }
+
+    S[] s2, s3;
+    foreach (ref el; chain(s2, s3))
+    {
+        int n = el.v;
+    }
+}
+
 /**
 Choose one of two ranges at runtime depending on a Boolean condition.
 
@@ -1825,6 +2098,23 @@ pure @safe unittest
     auto chosen2 = chosen.save;
 }
 
+pure @safe nothrow unittest
+{
+    static struct S {
+        int v;
+        @disable this(this);
+    }
+
+    auto a = [S(1), S(2), S(3)];
+    auto b = [S(4), S(5), S(6)];
+
+    auto chosen = choose(true, a, b);
+    assert(chosen.front.v == 1);
+
+    auto chosen2 = choose(false, a, b);
+    assert(chosen2.front.v == 4);
+}
+
 /**
 Choose one of multiple ranges at runtime.
 
@@ -2122,6 +2412,20 @@ pure @safe unittest
     assert(equal(r.save, "fboaor"));
     assert(equal(r.save, "fboaor"));
 }
+pure @safe nothrow unittest
+{
+    import std.algorithm.comparison : equal;
+
+    static struct S {
+        int v;
+        @disable this(this);
+    }
+
+    S[] a = [ S(1), S(2) ];
+    S[] b = [ S(10), S(20) ];
+    auto r = roundRobin(a, b);
+    assert(equal(r, [ S(1), S(10), S(2), S(20) ]));
+}
 
 /**
 Iterates a random-access range starting from a given point and
@@ -2313,12 +2617,12 @@ if (isInputRange!(Unqual!Range) &&
         /// ditto
         @property void front(ElementType!R v)
         {
+            import std.algorithm.mutation : move;
+
             assert(!empty,
                 "Attempting to assign to the front of an empty "
                 ~ Take.stringof);
-            // This has to return auto instead of void because of
-            // https://issues.dlang.org/show_bug.cgi?id=4706
-            source.front = v;
+            source.front = move(v);
         }
 
     static if (hasMobileElements!R)
@@ -2694,10 +2998,12 @@ if (isInputRange!R)
             {
                 @property auto ref front(ElementType!R v)
                 {
+                    import std.algorithm.mutation : move;
+
                     assert(!empty,
                         "Attempting to assign to the front of an empty "
                         ~ typeof(this).stringof);
-                    return _input.front = v;
+                    return _input.front = move(v);
                 }
             }
         }
@@ -3929,7 +4235,9 @@ if (isForwardRange!R && !isInfinite!R)
             /// ditto
             @property void front(ElementType!R val)
             {
-                _original[_index] = val;
+                import std.algorithm.mutation : move;
+
+                _original[_index] = move(val);
             }
         }
 
@@ -4039,7 +4347,9 @@ if (isForwardRange!R && !isInfinite!R)
             /// ditto
             @property auto front(ElementType!R val)
             {
-                return _current.front = val;
+                import std.algorithm.mutation : move;
+
+                return _current.front = move(val);
             }
         }
 
@@ -7053,7 +7363,9 @@ struct FrontTransversal(Ror,
     {
         @property void front(ElementType val)
         {
-            _input.front.front = val;
+            import std.algorithm.mutation : move;
+
+            _input.front.front = move(val);
         }
     }
 
@@ -7110,7 +7422,9 @@ struct FrontTransversal(Ror,
         {
             @property void back(ElementType val)
             {
-                _input.back.front = val;
+                import std.algorithm.mutation : move;
+
+                _input.back.front = move(val);
             }
         }
     }
@@ -7143,7 +7457,9 @@ struct FrontTransversal(Ror,
         {
             void opIndexAssign(ElementType val, size_t n)
             {
-                _input[n].front = val;
+                import std.algorithm.mutation : move;
+
+                _input[n].front = move(val);
             }
         }
         mixin ImplementLength!_input;
@@ -8785,7 +9101,8 @@ public:
         {
             // `nextSource` is used to "look one step into the future" and check for the end
             // this means `nextSource` is advanced by `stepSize` on every `popFront`
-            nextSource = source.save.drop(windowSize);
+            nextSource = source.save;
+            auto poppedElems = nextSource.popFrontN(windowSize);
         }
 
         if (source.empty)
@@ -8798,7 +9115,7 @@ public:
         {
             static if (needsEndTracker)
             {
-                if (nextSource.empty)
+                if (poppedElems < windowSize)
                     hasShownPartialBefore = true;
             }
             else
@@ -8806,14 +9123,13 @@ public:
                 if (source.length <= windowSize)
                     hasShownPartialBefore = true;
             }
-
         }
         else
         {
             // empty source range is needed, s.t. length, slicing etc. works properly
             static if (needsEndTracker)
             {
-                if (nextSource.empty)
+                if (poppedElems < windowSize)
                      _empty = true;
             }
             else
@@ -9770,6 +10086,15 @@ public:
     assert([1].map!(x => x).slide(2).equal!equal([[1]]));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=19642
+@safe unittest
+{
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : splitter;
+
+    assert("ab cd".splitter(' ').slide!(No.withPartial)(2).equal!equal([["ab", "cd"]]));
+}
+
 private struct OnlyResult(Values...)
 if (Values.length > 1)
 {
index 748fde38ed3b21657e4c83ea2e26ea7e7f71b23e..e581409ba32400f42006e7ce49b7af541c9b2fde 100644 (file)
@@ -1041,7 +1041,8 @@ See_Also:
  */
 enum bool isBidirectionalRange(R) = isForwardRange!R
     && is(typeof((R r) => r.popBack))
-    && is(typeof((R r) { return r.back; } (R.init)) == ElementType!R);
+    && (is(typeof((return ref R r) => r.back)) || is(typeof(ref (return ref R r) => r.back)))
+    && is(typeof(R.init.back.init) == ElementType!R);
 
 ///
 @safe unittest
index 21766fc2d0c0f751fb2e76578cd3c7e068653a71..ac73f70f0f347e903044172305a202dc6892b7f5 100644 (file)
@@ -73,12 +73,7 @@ final:
     override @property ref size_t refCount() { return _refCount; }
     override @property ref const(RegEx) pattern(){ return re; }
 
-    static if (__traits(hasMember,Stream, "search"))
-    {
-        enum kicked = true;
-    }
-    else
-        enum kicked = false;
+    enum kicked = __traits(hasMember, Stream, "search");
 
     static size_t initialMemory(const ref RegEx re)
     {
index ab28d37ecb714cc8e47b5272efbae6f222cb8447..f4643ae1bcb7bb0b6a61fd71cb54796cbe98ba39 100644 (file)
@@ -759,12 +759,7 @@ final:
 
     }
 
-    static if (__traits(hasMember,Stream, "search"))
-    {
-        enum kicked = true;
-    }
-    else
-        enum kicked = false;
+    enum kicked = __traits(hasMember, Stream, "search");
 
     static size_t getThreadSize(const ref Regex!Char re)
     {
index 92e4906b4f4a44a2b44e96bb05e46b5699c29276..5ed685fe7fcd3077db3a30da59e2533d1581f925 100644 (file)
@@ -43,6 +43,8 @@ License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
 Authors:   $(HTTP digitalmars.com, Walter Bright),
            $(HTTP erdani.org, Andrei Alexandrescu),
            Alex Rønne Petersen
+Macros:
+CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1)
  */
 module std.stdio;
 
@@ -534,8 +536,7 @@ Params:
     name = range or string representing the file _name
     stdioOpenmode = range or string represting the open mode
         (with the same semantics as in the C standard library
-        $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
-        function)
+        $(CSTDIO fopen) function)
 
 Throws: `ErrnoException` if the file could not be opened.
  */
@@ -618,8 +619,7 @@ file.
 /**
 Detaches from the current file (throwing on failure), and then attempts to
 _open file `name` with mode `stdioOpenmode`. The mode has the
-same semantics as in the C standard library $(HTTP
-cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
+same semantics as in the C standard library $(CSTDIO fopen) function.
 
 Throws: `ErrnoException` in case of error.
  */
@@ -735,8 +735,7 @@ Reuses the `File` object to either open a different file, or change
 the file mode. If `name` is `null`, the mode of the currently open
 file is changed; otherwise, a new file is opened, reusing the C
 `FILE*`. The function has the same semantics as in the C standard
-library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
-function.
+library $(CSTDIO freopen) function.
 
 Note: Calling `reopen` with a `null` `name` is not implemented
 in all C runtimes.
@@ -832,8 +831,8 @@ Throws: `ErrnoException` in case of error.
 Params:
     fd = File descriptor to associate with this `File`.
     stdioOpenmode = Mode to associate with this File. The mode has the same semantics
-        semantics as in the C standard library
-        $(HTTP cplusplus.com/reference/cstdio/fopen/, fdopen) function, and must be compatible with `fd`.
+        semantics as in the C standard library $(CSTDIO fdopen) function,
+        and must be compatible with `fd`.
  */
     void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
     {
@@ -932,8 +931,7 @@ Throws: `ErrnoException` in case of error.
     }
 
 /**
-Returns `true` if the file is at end (see $(HTTP
-cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
+Returns `true` if the file is at end (see $(CSTDIO feof)).
 
 Throws: `Exception` if the file is not opened.
  */
@@ -961,8 +959,7 @@ Throws: `Exception` if the file is not opened.
 
 /**
 If the file is closed or not yet opened, returns `true`. Otherwise, returns
-$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
-the file handle.
+$(CSTDIO ferror) for the file handle.
  */
     @property bool error() const @trusted pure nothrow
     {
@@ -1017,8 +1014,7 @@ Throws: `ErrnoException` on failure if closing the file.
 
 /**
 If the file was closed or not yet opened, succeeds vacuously. Otherwise
-closes the file (by calling $(HTTP
-cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
+closes the file (by calling $(CSTDIO fclose)),
 throwing on error. Even if an exception is thrown, afterwards the $(D
 File) object is empty. This is different from `detach` in that it
 always closes the file; consequently, all other `File` objects
@@ -1046,8 +1042,7 @@ Throws: `ErrnoException` on error.
 
 /**
 If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns
-$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
-_clearerr) for the file handle.
+$(CSTDIO clearerr) for the file handle.
  */
     void clearerr() @safe pure nothrow
     {
@@ -1058,8 +1053,7 @@ _clearerr) for the file handle.
 /**
 Flushes the C `FILE` buffers.
 
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
-for the file handle.
+Calls $(CSTDIO fflush) for the file handle.
 
 Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
  */
@@ -1125,7 +1119,7 @@ Throws: `Exception` if the file is not opened or if the OS call fails.
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
+Calls $(CSTDIO fread) for the
 file handle. The number of items to read and the size of
 each item is inferred from the size and type of the input array, respectively.
 
@@ -1220,7 +1214,7 @@ Throws: `ErrnoException` if the file is not opened or the call to `fread` fails.
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
+Calls $(CSTDIO fwrite) for the file
 handle. The number of items to write and the size of each
 item is inferred from the size and type of the input array, respectively. An
 error is thrown if the buffer could not be written in its entirety.
@@ -1290,7 +1284,7 @@ Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fa
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
+Calls $(CSTDIO fseek)
 for the file handle to move its position indicator.
 
 Params:
@@ -1374,7 +1368,7 @@ Throws: `Exception` if the file is not opened.
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/cstdio/ftell.html, ftell)
+Calls $(CSTDIO ftell)
 for the managed file handle, which returns the current value of
 the position indicator of the file handle.
 
@@ -1420,8 +1414,7 @@ Throws: `Exception` if the file is not opened.
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
-for the file handle.
+Calls $(CSTDIO rewind) for the file handle.
 
 Throws: `Exception` if the file is not opened.
  */
@@ -1434,8 +1427,7 @@ Throws: `Exception` if the file is not opened.
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
-the file handle.
+Calls $(CSTDIO setvbuf) for the file handle.
 
 Throws: `Exception` if the file is not opened.
         `ErrnoException` if the call to `setvbuf` fails.
@@ -1450,8 +1442,7 @@ Throws: `Exception` if the file is not opened.
     }
 
 /**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
-_setvbuf) for the file handle.
+Calls $(CSTDIO setvbuf) for the file handle.
 
 Throws: `Exception` if the file is not opened.
         `ErrnoException` if the call to `setvbuf` fails.
@@ -2252,8 +2243,7 @@ $(CONSOLE
     }
 
 /**
- Returns a temporary file by calling
- $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile).
+ Returns a temporary file by calling $(CSTDIO tmpfile).
  Note that the created file has no $(LREF name).*/
     static File tmpfile() @safe
     {
index f4d011bbad96ab3cac846d44c868885748bec398..689f0aef80971532f296c2be59ace71729e01fac 100644 (file)
@@ -1198,7 +1198,7 @@ if (isCallable!func)
 }
 
 /**
-Convert the result of `__traits(getParameterStorageClasses)`
+Convert the result of $(DDSUBLINK spec/traits, getParameterStorageClasses, `__traits(getParameterStorageClasses)`)
 to $(LREF ParameterStorageClass) `enum`s.
 
 Params:
@@ -2184,7 +2184,7 @@ if (isCallable!func)
     void func() {}
     static assert(variadicFunctionStyle!func == Variadic.no);
 
-    extern(C) int printf(in char*, ...);
+    extern(C) int printf(const char*, ...);
     static assert(variadicFunctionStyle!printf == Variadic.c);
 }
 
@@ -2572,6 +2572,8 @@ if (is(T == class))
 /**
 Determines whether `T` has its own context pointer.
 `T` must be either `class`, `struct`, or `union`.
+
+See also: $(DDSUBLINK spec/traits, isNested, `__traits(isNested, T)`)
 */
 template isNested(T)
 if (is(T == class) || is(T == struct) || is(T == union))
@@ -3857,6 +3859,8 @@ package alias Identity(alias A) = A;
 /**
    Yields `true` if and only if `T` is an aggregate that defines
    a symbol called `name`.
+
+   See also: $(DDSUBLINK spec/traits, hasMember, `__traits(hasMember, T, name)`)
  */
 enum hasMember(T, string name) = __traits(hasMember, T, name);
 
@@ -4839,6 +4843,8 @@ package enum maxAlignment(U...) =
 
 /**
 Returns class instance alignment.
+
+See also: $(DDSUBLINK spec/traits, classInstanceAlignment, `__traits(classInstanceAlignment, T)`)
  */
 template classInstanceAlignment(T)
 if (is(T == class))
@@ -5313,7 +5319,7 @@ enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lv
     }
     else
     {
-        mixin(q{ struct S6 { void opAssign(in ref S5); } });
+        mixin(q{ struct S6 { void opAssign(scope const ref S5); } });
 
         static assert(!isRvalueAssignable!(S6, S5));
         static assert( isLvalueAssignable!(S6, S5));
@@ -5680,7 +5686,7 @@ private struct __InoutWorkaroundStruct{}
 
 /**
 Creates an lvalue or rvalue of type `T` for `typeof(...)` and
-`__traits(compiles, ...)` purposes. No actual value is returned.
+$(DDSUBLINK spec/traits, compiles, `__traits(compiles, ...)`) purposes. No actual value is returned.
 
 Params:
     T = The type to transform
@@ -6166,7 +6172,7 @@ template BuiltinTypeOf(T)
 //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
 
 /**
- * Detect whether `T` is a built-in boolean type.
+ * Detect whether `T` is a built-in boolean type or enum of boolean base type.
  */
 enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool);
 
@@ -6196,8 +6202,15 @@ enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool);
 }
 
 /**
- * Detect whether `T` is a built-in integral type. Types `bool`,
- * `char`, `wchar`, and `dchar` are not considered integral.
+ * Detect whether `T` is a built-in integral type.
+ * Integral types are `byte`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `cent`, `ucent`,
+ * and enums with an integral type as its base type.
+ * Params:
+ *      T = type to test
+ * Returns:
+ *      `true` if `T` is an integral type
+ * Note:
+ *      this is not the same as $(LINK2 https://dlang.org/spec/traits.html#isIntegral, `__traits(isIntegral)`)
  */
 template isIntegral(T)
 {
@@ -6260,7 +6273,10 @@ template isIntegral(T)
 
 /**
  * Detect whether `T` is a built-in floating point type.
+ *
+ * See also: $(DDSUBLINK spec/traits, isFloating, `__traits(isFloating, T)`)
  */
+// is(T : real) to discount complex types
 enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);
 
 ///
@@ -6398,7 +6414,10 @@ template isNumeric(T)
 /**
  * Detect whether `T` is a scalar type (a built-in numeric, character or
  * boolean type).
+ *
+ * See also: $(DDSUBLINK spec/traits, isScalar, `__traits(isScalar, T)`)
  */
+// is(T : real) to discount complex types
 enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real);
 
 ///
@@ -6927,6 +6946,8 @@ template isAutodecodableString(T)
 
 /**
  * Detect whether type `T` is a static array.
+ *
+ * See also: $(DDSUBLINK spec/traits, isStaticArray, `__traits(isStaticArray, T)`)
  */
 enum bool isStaticArray(T) = __traits(isStaticArray, T);
 
@@ -7056,6 +7077,8 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;
 
 /**
  * Detect whether `T` is an associative array type
+ *
+ * See also: $(DDSUBLINK spec/traits, isAssociativeArray, `__traits(isAssociativeArray, T)`)
  */
 enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
 
@@ -7542,14 +7565,12 @@ template isCallable(alias callable)
     else static if (is(typeof(&callable.opCall) V : V*) && is(V == function))
         // T is a type which has a static member function opCall().
         enum bool isCallable = true;
-    else static if (is(typeof(&callable.opCall!())))
+    else static if (is(typeof(&callable.opCall!()) TemplateInstanceType))
     {
-        alias TemplateInstanceType = typeof(&callable.opCall!());
         enum bool isCallable = isCallable!TemplateInstanceType;
     }
-    else static if (is(typeof(&callable!())))
+    else static if (is(typeof(&callable!()) TemplateInstanceType))
     {
-        alias TemplateInstanceType = typeof(&callable!());
         enum bool isCallable = isCallable!TemplateInstanceType;
     }
     else
@@ -7613,14 +7634,15 @@ template isCallable(alias callable)
 
 
 /**
-Detect whether `T` is an abstract function.
+Detect whether `S` is an abstract function.
 
+See also: $(DDSUBLINK spec/traits, isAbstractFunction, `__traits(isAbstractFunction, S)`)
 Params:
-    T = The type to check
+    S = The symbol to check
 Returns:
     A `bool`
  */
-enum isAbstractFunction(alias T) = __traits(isAbstractFunction, T);
+enum isAbstractFunction(alias S) = __traits(isAbstractFunction, S);
 
 ///
 @safe unittest
@@ -7635,9 +7657,11 @@ enum isAbstractFunction(alias T) = __traits(isAbstractFunction, T);
 }
 
 /**
- * Detect whether `T` is a final function.
+ * Detect whether `S` is a final function.
+ *
+ * See also: $(DDSUBLINK spec/traits, isFinalFunction, `__traits(isFinalFunction, S)`)
  */
-enum isFinalFunction(alias T) = __traits(isFinalFunction, T);
+enum isFinalFunction(alias S) = __traits(isFinalFunction, S);
 
 ///
 @safe unittest
@@ -7703,9 +7727,11 @@ template isNestedFunction(alias f)
 }
 
 /**
- * Detect whether `T` is an abstract class.
+ * Detect whether `S` is an abstract class.
+ *
+ * See also: $(DDSUBLINK spec/traits, isAbstractClass, `__traits(isAbstractClass, S)`)
  */
-enum isAbstractClass(alias T) = __traits(isAbstractClass, T);
+enum isAbstractClass(alias S) = __traits(isAbstractClass, S);
 
 ///
 @safe unittest
@@ -7723,9 +7749,11 @@ enum isAbstractClass(alias T) = __traits(isAbstractClass, T);
 }
 
 /**
- * Detect whether `T` is a final class.
+ * Detect whether `S` is a final class.
+ *
+ * See also: $(DDSUBLINK spec/traits, isFinalClass, `__traits(isFinalClass, S)`)
  */
-enum isFinalClass(alias T) = __traits(isFinalClass, T);
+enum isFinalClass(alias S) = __traits(isFinalClass, S);
 
 ///
 @safe unittest
@@ -9092,12 +9120,13 @@ template isFinal(alias X)
  + If a type cannot be copied, then code such as `MyStruct x; auto y = x;` will fail to compile.
  + Copying for structs can be disabled by using `@disable this(this)`.
  +
+ + See also: $(DDSUBLINK spec/traits, isCopyable, `__traits(isCopyable, S)`)
  + Params:
  +  S = The type to check.
  +
  + Returns:
  +  `true` if `S` can be copied. `false` otherwise.
- + ++/
+ +/
 enum isCopyable(S) = __traits(isCopyable, S);
 
 ///
index bde8439a5fca872bafc31dc0cc0efb456588c912..d267e711e6a645e6c4c1e19dfa971f4897b80734 100644 (file)
@@ -2332,7 +2332,7 @@ break the soundness of D's type system and does not incur any of the
 risks usually associated with `cast`.
 
 Params:
-    T = An object, interface, array slice type, or associative array type.
+    T = Any type.
  */
 template Rebindable(T)
 if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
@@ -2395,15 +2395,155 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
     static assert(!__traits(compiles, &r.get()));
 }
 
+/// ditto
+struct Rebindable(T)
+if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T)
+{
+private:
+    static if (isAssignable!(typeof(cast() T.init)))
+    {
+        enum useQualifierCast = true;
+
+        typeof(cast() T.init) data;
+    }
+    else
+    {
+        enum useQualifierCast = false;
+
+        align(T.alignof)
+        static struct Payload
+        {
+            static if (hasIndirections!T)
+            {
+                void[T.sizeof] data;
+            }
+            else
+            {
+                ubyte[T.sizeof] data;
+            }
+        }
+
+        Payload data;
+    }
+
+public:
+
+    static if (!__traits(compiles, { T value; }))
+    {
+        @disable this();
+    }
+
+    /**
+     * Constructs a `Rebindable` from a given value.
+     */
+    this(T value) @trusted
+    {
+        static if (useQualifierCast)
+        {
+            this.data = cast() value;
+        }
+        else
+        {
+            set(value);
+        }
+    }
+
+    /**
+     * Overwrites the currently stored value with `value`.
+     */
+    void opAssign(this This)(T value) @trusted
+    {
+        clear;
+        set(value);
+    }
+
+    /**
+     * Returns the value currently stored in the `Rebindable`.
+     */
+    T get(this This)() @property @trusted
+    {
+        static if (useQualifierCast)
+        {
+            return cast(T) this.data;
+        }
+        else
+        {
+            return *cast(T*) &this.data;
+        }
+    }
+
+    static if (!useQualifierCast)
+    {
+        ~this() @trusted
+        {
+            clear;
+        }
+    }
+
+    ///
+    alias get this;
+
+private:
+
+    void set(this This)(T value)
+    {
+        static if (useQualifierCast)
+        {
+            this.data = cast() value;
+        }
+        else
+        {
+            // As we're escaping a copy of `value`, deliberately leak a copy:
+            static union DontCallDestructor
+            {
+                T value;
+            }
+            DontCallDestructor copy = DontCallDestructor(value);
+            this.data = *cast(Payload*) &copy;
+        }
+    }
+
+    void clear(this This)()
+    {
+        // work around reinterpreting cast being impossible in CTFE
+        if (__ctfe)
+        {
+            return;
+        }
+
+        // call possible struct destructors
+        .destroy!(No.initialize)(*cast(T*) &this.data);
+    }
+}
+
+/// Using Rebindable in a generic algorithm:
 @safe unittest
 {
-    class CustomToHash
+    import std.range.primitives : front, popFront;
+
+    // simple version of std.algorithm.searching.maxElement
+    typeof(R.init.front) maxElement(R)(R r)
     {
-        override size_t toHash() const nothrow @trusted { return 42; }
+        auto max = rebindable(r.front);
+        r.popFront;
+        foreach (e; r)
+            if (e > max)
+                max = e; // Rebindable allows const-correct reassignment
+        return max;
     }
-    Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash();
-    assert(a.toHash() == 42, "Rebindable!A should offer toHash()"
-        ~ " by forwarding to A.toHash().");
+    struct S
+    {
+        char[] arr;
+        alias arr this; // for comparison
+    }
+    // can't convert to mutable
+    const S cs;
+    static assert(!__traits(compiles, { S s = cs; }));
+
+    alias CS = const S;
+    CS[] arr = [CS("harp"), CS("apple"), CS("pot")];
+    CS ms = maxElement(arr);
+    assert(ms.arr == "pot");
 }
 
 // https://issues.dlang.org/show_bug.cgi?id=18615
@@ -2453,6 +2593,34 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
         ~ " comparable against Object itself and use Object.opEquals.");
 }
 
+///
+@system unittest
+{
+    static struct S
+    {
+        int* ptr;
+    }
+    S s = S(new int);
+
+    const cs = s;
+    // Can't assign s.ptr to cs.ptr
+    static assert(!__traits(compiles, {s = cs;}));
+
+    Rebindable!(const S) rs = s;
+    assert(rs.ptr is s.ptr);
+    // rs.ptr is const
+    static assert(!__traits(compiles, {rs.ptr = null;}));
+
+    // Can't assign s.ptr to rs.ptr
+    static assert(!__traits(compiles, {s = rs;}));
+
+    const S cs2 = rs;
+    // Rebind rs
+    rs = cs2;
+    rs = S();
+    assert(rs.ptr is null);
+}
+
 // https://issues.dlang.org/show_bug.cgi?id=18755
 @safe unittest
 {
@@ -2473,13 +2641,145 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
     }));
 }
 
+@safe unittest
+{
+    class CustomToHash
+    {
+        override size_t toHash() const nothrow @trusted { return 42; }
+    }
+    Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash();
+    assert(a.toHash() == 42, "Rebindable!A should offer toHash()"
+        ~ " by forwarding to A.toHash().");
+}
+
+// Test Rebindable!immutable
+@safe unittest
+{
+    static struct S
+    {
+        int* ptr;
+    }
+    S s = S(new int);
+
+    Rebindable!(immutable S) ri = S(new int);
+    assert(ri.ptr !is null);
+    static assert(!__traits(compiles, {ri.ptr = null;}));
+
+    // ri is not compatible with mutable S
+    static assert(!__traits(compiles, {s = ri;}));
+    static assert(!__traits(compiles, {ri = s;}));
+
+    auto ri2 = ri;
+    assert(ri2.ptr == ri.ptr);
+
+    const S cs3 = ri;
+    static assert(!__traits(compiles, {ri = cs3;}));
+
+    immutable S si = ri;
+    // Rebind ri
+    ri = si;
+    ri = S();
+    assert(ri.ptr is null);
+
+    // Test RB!immutable -> RB!const
+    Rebindable!(const S) rc = ri;
+    assert(rc.ptr is null);
+    ri = S(new int);
+    rc = ri;
+    assert(rc.ptr !is null);
+
+    // test rebindable, opAssign
+    rc.destroy;
+    assert(rc.ptr is null);
+    rc = rebindable(cs3);
+    rc = rebindable(si);
+    assert(rc.ptr !is null);
+
+    ri.destroy;
+    assert(ri.ptr is null);
+    ri = rebindable(si);
+    assert(ri.ptr !is null);
+}
+
+// Test disabled default ctor
+@safe unittest
+{
+    static struct ND
+    {
+        int i;
+        @disable this();
+        this(int i) inout {this.i = i;}
+    }
+    static assert(!__traits(compiles, Rebindable!ND()));
+
+    Rebindable!(const ND) rb = const ND(1);
+    assert(rb.i == 1);
+    rb = immutable ND(2);
+    assert(rb.i == 2);
+    rb = rebindable(const ND(3));
+    assert(rb.i == 3);
+    static assert(!__traits(compiles, rb.i++));
+}
+
+// Test copying
+@safe unittest
+{
+    int del;
+    int post;
+    struct S
+    {
+        int* ptr;
+        int level;
+        this(this)
+        {
+            post++;
+            level++;
+        }
+        ~this()
+        {
+            del++;
+        }
+    }
+
+    // test ref count
+    {
+        Rebindable!S rc = S(new int);
+    }
+    assert(post == del - 1);
+}
+
+@safe unittest
+{
+    int del;
+    int post;
+    struct S
+    {
+        immutable int x;
+        int level;
+        this(this)
+        {
+            post++;
+            level++;
+        }
+        ~this()
+        {
+            del++;
+        }
+    }
+
+    // test ref count
+    {
+        Rebindable!S rc = S(0);
+    }
+    assert(post == del - 1);
+}
+
 /**
 Convenience function for creating a `Rebindable` using automatic type
 inference.
 
 Params:
-    obj = A reference to an object, interface, associative array, or an array slice
-          to initialize the `Rebindable` with.
+    obj = A reference to a value to initialize the `Rebindable` with.
 
 Returns:
     A newly constructed `Rebindable` initialized with the given reference.
@@ -2514,6 +2814,26 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
     assert(c3.payload == 2);
 }
 
+/// ditto
+Rebindable!T rebindable(T)(T value)
+if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T
+    && !is(T : Rebindable!U, U))
+{
+    return Rebindable!T(value);
+}
+
+///
+@safe unittest
+{
+    immutable struct S
+    {
+        int[] array;
+    }
+    auto s1 = [3].idup.rebindable;
+    s1 = [4].idup.rebindable;
+    assert(s1 == [4]);
+}
+
 /**
 This function simply returns the `Rebindable` object passed in.  It's useful
 in generic programming cases when a given object may be either a regular
@@ -2626,10 +2946,6 @@ Rebindable!T rebindable(T)(Rebindable!T obj)
         static assert(is(Rebindable!(T[]) == T[]));
     }
 
-    // https://issues.dlang.org/show_bug.cgi?id=12046
-    static assert(!__traits(compiles, Rebindable!(int[1])));
-    static assert(!__traits(compiles, Rebindable!(const int[1])));
-
     // Pull request 3341
     Rebindable!(immutable int[int]) pr3341 = [123:345];
     assert(pr3341[123] == 345);