]> gcc.gnu.org Git - gcc.git/commitdiff
d: Merge upstream dmd d579c467c1, phobos 88aa69b14.
authorIain Buclaw <ibuclaw@gdcproject.org>
Tue, 27 Sep 2022 08:43:32 +0000 (10:43 +0200)
committerIain Buclaw <ibuclaw@gdcproject.org>
Tue, 27 Sep 2022 08:50:18 +0000 (10:50 +0200)
D front-end changes:

    - Throwing from contracts of `nothrow' functions has been
      deprecated, as this breaks the guarantees of `nothrow'.
    - Added language support for initializing the interior pointer of
      associative arrays using `new' keyword.

Phobos changes:

    - The std.digest.digest module has been removed.
    - The std.xml module has been removed.

gcc/d/ChangeLog:

* dmd/MERGE: Merge upstream dmd d579c467c1.
* decl.cc (layout_struct_initializer): Update for new front-end
interface.
* expr.cc (ExprVisitor::visit (AssignExp *)): Remove lowering of array
assignments.
(ExprVisitor::visit (NewExp *)): Add new lowering of new'ing
associative arrays to an _aaNew() library call.
* runtime.def (ARRAYSETASSIGN): Remove.
(AANEW): Define.

libphobos/ChangeLog:

* libdruntime/MERGE: Merge upstream druntime d579c467c1.
* libdruntime/Makefile.am (DRUNTIME_DSOURCES): Remove
rt/arrayassign.d.
* libdruntime/Makefile.in: Regenerate.
* src/MERGE: Merge upstream phobos 88aa69b14.
* src/Makefile.am (PHOBOS_DSOURCES): Remove std/digest/digest.d,
std/xml.d.
* src/Makefile.in: Regenerate.

171 files changed:
gcc/d/decl.cc
gcc/d/dmd/MERGE
gcc/d/dmd/aggregate.d
gcc/d/dmd/aggregate.h
gcc/d/dmd/apply.d
gcc/d/dmd/arrayop.d
gcc/d/dmd/attrib.d
gcc/d/dmd/canthrow.d
gcc/d/dmd/chkformat.d
gcc/d/dmd/clone.d
gcc/d/dmd/cparse.d
gcc/d/dmd/dcast.d
gcc/d/dmd/declaration.h
gcc/d/dmd/dimport.d
gcc/d/dmd/dinterpret.d
gcc/d/dmd/dmangle.d
gcc/d/dmd/doc.d
gcc/d/dmd/dsymbol.d
gcc/d/dmd/dsymbol.h
gcc/d/dmd/dsymbolsem.d
gcc/d/dmd/dtemplate.d
gcc/d/dmd/escape.d
gcc/d/dmd/expression.d
gcc/d/dmd/expression.h
gcc/d/dmd/expressionsem.d
gcc/d/dmd/func.d
gcc/d/dmd/iasmgcc.d
gcc/d/dmd/id.d
gcc/d/dmd/init.d
gcc/d/dmd/init.h
gcc/d/dmd/initsem.d
gcc/d/dmd/lexer.d
gcc/d/dmd/module.h
gcc/d/dmd/mtype.d
gcc/d/dmd/mtype.h
gcc/d/dmd/opover.d
gcc/d/dmd/parse.d
gcc/d/dmd/root/object.h
gcc/d/dmd/semantic3.d
gcc/d/dmd/transitivevisitor.d
gcc/d/dmd/typesem.d
gcc/d/expr.cc
gcc/d/runtime.def
gcc/testsuite/gdc.test/compilable/commontype.d
gcc/testsuite/gdc.test/compilable/imports/cimports2a.i [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/imports/cimports2b.i [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/imports/format23327.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/imports/format23327/write.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/segfaultgolf.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/statictemplatethis.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test13123.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21243.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21956.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test22674.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23173.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23258.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23306.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23327.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/vararg.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/diag10169.d
gcc/testsuite/gdc.test/fail_compilation/diag10783.d
gcc/testsuite/gdc.test/fail_compilation/diag13528.d
gcc/testsuite/gdc.test/fail_compilation/diag14145.d
gcc/testsuite/gdc.test/fail_compilation/diag15713.d
gcc/testsuite/gdc.test/fail_compilation/diag23355.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/diag3438.d
gcc/testsuite/gdc.test/fail_compilation/diag3438b.d [deleted file]
gcc/testsuite/gdc.test/fail_compilation/diag8894.d
gcc/testsuite/gdc.test/fail_compilation/dip22a.d
gcc/testsuite/gdc.test/fail_compilation/e15876_1.d
gcc/testsuite/gdc.test/fail_compilation/e15876_3.d
gcc/testsuite/gdc.test/fail_compilation/e15876_4.d
gcc/testsuite/gdc.test/fail_compilation/fail10968.d
gcc/testsuite/gdc.test/fail_compilation/fail121.d
gcc/testsuite/gdc.test/fail_compilation/fail13123.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail17646.d
gcc/testsuite/gdc.test/fail_compilation/fail18892.d
gcc/testsuite/gdc.test/fail_compilation/fail18970.d
gcc/testsuite/gdc.test/fail_compilation/fail18979.d
gcc/testsuite/gdc.test/fail_compilation/fail19103.d
gcc/testsuite/gdc.test/fail_compilation/fail19687.d
gcc/testsuite/gdc.test/fail_compilation/fail19913.d
gcc/testsuite/gdc.test/fail_compilation/fail21243.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail23109.d
gcc/testsuite/gdc.test/fail_compilation/fail7372.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/faildottypeinfo.d
gcc/testsuite/gdc.test/fail_compilation/failoffset.d
gcc/testsuite/gdc.test/fail_compilation/ice10938.d
gcc/testsuite/gdc.test/fail_compilation/ice12174.d
gcc/testsuite/gdc.test/fail_compilation/ice15855.d
gcc/testsuite/gdc.test/fail_compilation/ice18469.d
gcc/testsuite/gdc.test/fail_compilation/ice19755.d
gcc/testsuite/gdc.test/fail_compilation/imports/fail7372.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
gcc/testsuite/gdc.test/fail_compilation/mixinprop.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test15785.d
gcc/testsuite/gdc.test/fail_compilation/test15897.d
gcc/testsuite/gdc.test/fail_compilation/test16188.d
gcc/testsuite/gdc.test/fail_compilation/test17380spec.d
gcc/testsuite/gdc.test/fail_compilation/test21096.d
gcc/testsuite/gdc.test/fail_compilation/test22680.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/newaa.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test23234.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/testassign.d
libphobos/libdruntime/MERGE
libphobos/libdruntime/Makefile.am
libphobos/libdruntime/Makefile.in
libphobos/libdruntime/__builtins.di
libphobos/libdruntime/core/demangle.d
libphobos/libdruntime/core/internal/array/arrayassign.d
libphobos/libdruntime/core/internal/dassert.d
libphobos/libdruntime/core/internal/utf.d
libphobos/libdruntime/core/simd.d
libphobos/libdruntime/core/stdc/fenv.d
libphobos/libdruntime/core/stdc/math.d
libphobos/libdruntime/core/sys/darwin/mach/loader.d
libphobos/libdruntime/core/sys/linux/fcntl.d
libphobos/libdruntime/core/sys/posix/dlfcn.d
libphobos/libdruntime/core/sys/posix/mqueue.d
libphobos/libdruntime/core/sys/posix/setjmp.d
libphobos/libdruntime/core/sys/posix/stdlib.d
libphobos/libdruntime/core/sys/posix/sys/types.d
libphobos/libdruntime/core/sys/windows/stacktrace.d
libphobos/libdruntime/core/time.d
libphobos/libdruntime/object.d
libphobos/libdruntime/rt/arrayassign.d [deleted file]
libphobos/libdruntime/rt/lifetime.d
libphobos/src/MERGE
libphobos/src/Makefile.am
libphobos/src/Makefile.in
libphobos/src/index.dd
libphobos/src/std/algorithm/comparison.d
libphobos/src/std/algorithm/iteration.d
libphobos/src/std/algorithm/searching.d
libphobos/src/std/algorithm/sorting.d
libphobos/src/std/compiler.d
libphobos/src/std/concurrency.d
libphobos/src/std/container/array.d
libphobos/src/std/container/dlist.d
libphobos/src/std/conv.d
libphobos/src/std/datetime/stopwatch.d
libphobos/src/std/datetime/timezone.d
libphobos/src/std/digest/crc.d
libphobos/src/std/digest/digest.d [deleted file]
libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d
libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
libphobos/src/std/experimental/allocator/building_blocks/free_list.d
libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
libphobos/src/std/experimental/allocator/building_blocks/region.d
libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
libphobos/src/std/experimental/allocator/building_blocks/segregator.d
libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
libphobos/src/std/experimental/allocator/package.d
libphobos/src/std/file.d
libphobos/src/std/format/package.d
libphobos/src/std/internal/math/gammafunction.d
libphobos/src/std/logger/core.d
libphobos/src/std/net/curl.d
libphobos/src/std/numeric.d
libphobos/src/std/path.d
libphobos/src/std/range/primitives.d
libphobos/src/std/socket.d
libphobos/src/std/stdio.d
libphobos/src/std/sumtype.d
libphobos/src/std/typecons.d
libphobos/src/std/uni/package.d
libphobos/src/std/utf.d
libphobos/src/std/xml.d [deleted file]

index e91aee30845a1682c0507f57ad5ceac4b40c70c9..dcfca648e4413cdb310ec363f2dd49783b7e8a0a 100644 (file)
@@ -2335,7 +2335,7 @@ layout_struct_initializer (StructDeclaration *sd)
 {
   StructLiteralExp *sle = StructLiteralExp::create (sd->loc, sd, NULL);
 
-  if (!sd->fill (sd->loc, sle->elements, true))
+  if (!sd->fill (sd->loc, *sle->elements, true))
     gcc_unreachable ();
 
   sle->type = sd->type;
index 85fc49d3d0a2e8f62f298079c618beec2a09b803..a4c46f3306ede57b9563edfe93b88cb6026ed42b 100644 (file)
@@ -1,4 +1,4 @@
-817610b16d0f0f469b9fbb28c000956fb910c43f
+4219ba670ce9ff92f3e874f0f048f2c28134c008
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
index f4b5e8af7ff52fe90a59f3c9c1d817809da6eb35..edca17fbad1edf3b0218d3d1878457db79452412 100644 (file)
@@ -355,23 +355,22 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
      *      false if any errors occur.
      *      Otherwise, returns true and the missing arguments will be pushed in elements[].
      */
-    final bool fill(const ref Loc loc, Expressions* elements, bool ctorinit)
+    final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit)
     {
         //printf("AggregateDeclaration::fill() %s\n", toChars());
         assert(sizeok == Sizeok.done);
-        assert(elements);
         const nfields = nonHiddenFields();
         bool errors = false;
 
         size_t dim = elements.dim;
         elements.setDim(nfields);
         foreach (size_t i; dim .. nfields)
-            (*elements)[i] = null;
+            elements[i] = null;
 
         // Fill in missing any elements with default initializers
         foreach (i; 0 .. nfields)
         {
-            if ((*elements)[i])
+            if (elements[i])
                 continue;
 
             auto vd = fields[i];
@@ -389,7 +388,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
                 if (!vd.isOverlappedWith(v2))
                     continue;
 
-                if ((*elements)[j])
+                if (elements[j])
                 {
                     vx = null;
                     break;
@@ -489,10 +488,10 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
                     else
                         e = telem.defaultInitLiteral(loc);
                 }
-                (*elements)[fieldi] = e;
+                elements[fieldi] = e;
             }
         }
-        foreach (e; *elements)
+        foreach (e; elements)
         {
             if (e && e.op == EXP.error)
                 return false;
index d91e35ee8c25b94e64613d8e19b4452ae6301f5f..f0909e3dc02aaf0ffbe8fc67cdade7d3b4e6f39a 100644 (file)
@@ -125,7 +125,7 @@ public:
     bool determineSize(const Loc &loc);
     virtual void finalizeSize() = 0;
     uinteger_t size(const Loc &loc) override final;
-    bool fill(const Loc &loc, Expressions *elements, bool ctorinit);
+    bool fill(const Loc &loc, Expressions &elements, bool ctorinit);
     Type *getType() override final;
     bool isDeprecated() const override final; // is aggregate deprecated?
     void setDeprecated();
index ac2c80ef64e427e4ecb9736dd191ff16d634826a..3b73771ef22f4875cc638e455547b1a19753443f 100644 (file)
@@ -16,6 +16,7 @@ import dmd.dsymbol;
 import dmd.dsymbolsem;
 import dmd.dtemplate;
 import dmd.expression;
+import dmd.root.array;
 import dmd.visitor;
 
 bool walkPostorder(Expression e, StoppableVisitor v)
@@ -86,12 +87,10 @@ public:
         return stop;
     }
 
-    bool doCond(Expressions* e)
+    extern(D) bool doCond(Expression[] e)
     {
-        if (!e)
-            return false;
-        for (size_t i = 0; i < e.dim && !stop; i++)
-            doCond((*e)[i]);
+        for (size_t i = 0; i < e.length && !stop; i++)
+            doCond(e[i]);
         return stop;
     }
 
@@ -110,13 +109,13 @@ public:
     override void visit(NewExp e)
     {
         //printf("NewExp::apply(): %s\n", toChars());
-        doCond(e.thisexp) || doCond(e.arguments) || applyTo(e);
+        doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e);
     }
 
     override void visit(NewAnonClassExp e)
     {
         //printf("NewAnonClassExp::apply(): %s\n", toChars());
-        doCond(e.thisexp) || doCond(e.arguments) || applyTo(e);
+        doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e);
     }
 
     override void visit(TypeidExp e)
@@ -143,13 +142,13 @@ public:
     override void visit(CallExp e)
     {
         //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
-        doCond(e.e1) || doCond(e.arguments) || applyTo(e);
+        doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e);
     }
 
     override void visit(ArrayExp e)
     {
         //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
-        doCond(e.e1) || doCond(e.arguments) || applyTo(e);
+        doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e);
     }
 
     override void visit(SliceExp e)
@@ -159,12 +158,12 @@ public:
 
     override void visit(ArrayLiteralExp e)
     {
-        doCond(e.basis) || doCond(e.elements) || applyTo(e);
+        doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e);
     }
 
     override void visit(AssocArrayLiteralExp e)
     {
-        doCond(e.keys) || doCond(e.values) || applyTo(e);
+        doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e);
     }
 
     override void visit(StructLiteralExp e)
@@ -173,13 +172,13 @@ public:
             return;
         int old = e.stageflags;
         e.stageflags |= stageApply;
-        doCond(e.elements) || applyTo(e);
+        doCond(e.elements.peekSlice()) || applyTo(e);
         e.stageflags = old;
     }
 
     override void visit(TupleExp e)
     {
-        doCond(e.e0) || doCond(e.exps) || applyTo(e);
+        doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e);
     }
 
     override void visit(CondExp e)
index 272e751cb51b51c15beadbc509724c1081aa7546..f07a6f44d2b681439119447d25222d76427d657d 100644 (file)
@@ -129,8 +129,7 @@ Expression arrayOp(BinExp e, Scope* sc)
         return arrayOpInvalidError(e);
 
     auto tiargs = new Objects();
-    auto args = new Expressions();
-    buildArrayOp(sc, e, tiargs, args);
+    auto args = buildArrayOp(sc, e, tiargs);
 
     import dmd.dtemplate : TemplateDeclaration;
     __gshared TemplateDeclaration arrayOp;
@@ -184,7 +183,7 @@ Expression arrayOp(BinAssignExp e, Scope* sc)
  * using reverse polish notation (RPN) to encode order of operations.
  * Encode operations as string arguments, using a "u" prefix for unary operations.
  */
-private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions* args)
+private Expressions* buildArrayOp(Scope* sc, Expression e, Objects* tiargs)
 {
     extern (C++) final class BuildArrayOpVisitor : Visitor
     {
@@ -194,11 +193,11 @@ private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions*
         Expressions* args;
 
     public:
-        extern (D) this(Scope* sc, Objects* tiargs, Expressions* args)
+        extern (D) this(Scope* sc, Objects* tiargs)
         {
             this.sc = sc;
             this.tiargs = tiargs;
-            this.args = args;
+            this.args = new Expressions();
         }
 
         override void visit(Expression e)
@@ -252,8 +251,9 @@ private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions*
         }
     }
 
-    scope v = new BuildArrayOpVisitor(sc, tiargs, args);
+    scope v = new BuildArrayOpVisitor(sc, tiargs);
     e.accept(v);
+    return v.args;
 }
 
 /***********************************************
index b569a9c3b466811dd63e67d2fa84530331758113..3472d1ce478928d0bc274c7b8aea2d6e0b76da18 100644 (file)
@@ -1431,7 +1431,7 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration
         if (auto sc = _scope)
         {
             _scope = null;
-            arrayExpressionSemantic(atts, sc);
+            arrayExpressionSemantic(atts.peekSlice(), sc);
         }
         auto exps = new Expressions();
         if (userAttribDecl && userAttribDecl !is this)
@@ -1554,7 +1554,7 @@ int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg)
         return 0;
 
     auto udas = sym.userAttribDecl.getAttributes();
-    arrayExpressionSemantic(udas, sc, true);
+    arrayExpressionSemantic(udas.peekSlice(), sc, true);
 
     return udas.each!((uda) {
         if (!uda.isTupleExp())
index 088ca61537e9a4373750653418b81d3bbc4e589c..09e38331137ed2670cfb1c7cd692a37f66018253 100644 (file)
@@ -111,13 +111,9 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN
                     auto ts = tbNext.baseElemOf().isTypeStruct();
                     if (ts)
                     {
-                        import dmd.id : Id;
-
                         auto sd = ts.sym;
                         const id = ce.f.ident;
-                        if (sd.postblit &&
-                            (id == Id._d_arrayctor || id == Id._d_arraysetctor ||
-                            id == Id._d_arrayassign_l || id == Id._d_arrayassign_r))
+                        if (sd.postblit && isArrayConstructionOrAssign(id))
                         {
                             checkFuncThrows(ce, sd.postblit);
                             return;
index e118d7014f959f203343c0dd4726f36b370e1f10..8204961fd2941eb9529aca38287007c9f43f3746 100644 (file)
@@ -1079,386 +1079,210 @@ Format parseGenericFormatSpecifier(scope const char[] format,
     return specifier; // success
 }
 
-unittest
+@("parseGenericFormatSpecifier") unittest
 {
-    /* parseGenericFormatSpecifier
-     */
-
     char genSpecifier;
     size_t idx;
 
-    assert(parseGenericFormatSpecifier("hhd", idx, genSpecifier) == Format.hhd);
-    assert(genSpecifier == 'd');
-
-    idx = 0;
-    assert(parseGenericFormatSpecifier("hn", idx, genSpecifier) == Format.hn);
-    assert(genSpecifier == 'n');
-
-    idx = 0;
-    assert(parseGenericFormatSpecifier("ji", idx, genSpecifier) == Format.jd);
-    assert(genSpecifier == 'i');
+    void testG(string fmtStr, Format expectedFormat, char expectedGenSpecifier)
+    {
+        idx = 0;
+        assert(parseGenericFormatSpecifier(fmtStr, idx, genSpecifier) == expectedFormat);
+        assert(genSpecifier == expectedGenSpecifier);
+    }
 
-    idx = 0;
-    assert(parseGenericFormatSpecifier("lu", idx, genSpecifier) == Format.lu);
-    assert(genSpecifier == 'u');
+    testG("hhd", Format.hhd, 'd');
+    testG("hn", Format.hn, 'n');
+    testG("ji", Format.jd, 'i');
+    testG("lu", Format.lu, 'u');
 
     idx = 0;
     assert(parseGenericFormatSpecifier("k", idx, genSpecifier) == Format.error);
+}
 
-    /* parsePrintfFormatSpecifier
-     */
-
-     bool widthStar;
-     bool precisionStar;
-
-     // one for each Format
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%d", idx, widthStar, precisionStar) == Format.d);
-     assert(idx == 2);
-     assert(!widthStar && !precisionStar);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%ld", idx, widthStar, precisionStar) == Format.ld);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%lld", idx, widthStar, precisionStar) == Format.lld);
-     assert(idx == 4);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%jd", idx, widthStar, precisionStar) == Format.jd);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%zd", idx, widthStar, precisionStar) == Format.zd);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%td", idx, widthStar, precisionStar) == Format.td);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%g", idx, widthStar, precisionStar) == Format.g);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%Lg", idx, widthStar, precisionStar) == Format.Lg);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%p", idx, widthStar, precisionStar) == Format.p);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%n", idx, widthStar, precisionStar) == Format.n);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%ln", idx, widthStar, precisionStar) == Format.ln);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%lln", idx, widthStar, precisionStar) == Format.lln);
-     assert(idx == 4);
+@("parsePrintfFormatSpecifier") unittest
+{
+    bool useGNUExts = false;
 
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%hn", idx, widthStar, precisionStar) == Format.hn);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%hhn", idx, widthStar, precisionStar) == Format.hhn);
-     assert(idx == 4);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%jn", idx, widthStar, precisionStar) == Format.jn);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%zn", idx, widthStar, precisionStar) == Format.zn);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%tn", idx, widthStar, precisionStar) == Format.tn);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%c", idx, widthStar, precisionStar) == Format.c);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%lc", idx, widthStar, precisionStar) == Format.lc);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%s", idx, widthStar, precisionStar) == Format.s);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%ls", idx, widthStar, precisionStar) == Format.ls);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%%", idx, widthStar, precisionStar) == Format.percent);
-     assert(idx == 2);
-
-     // Synonyms
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%i", idx, widthStar, precisionStar) == Format.d);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%u", idx, widthStar, precisionStar) == Format.u);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%o", idx, widthStar, precisionStar) == Format.u);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%x", idx, widthStar, precisionStar) == Format.u);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%X", idx, widthStar, precisionStar) == Format.u);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%f", idx, widthStar, precisionStar) == Format.g);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%F", idx, widthStar, precisionStar) == Format.g);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%G", idx, widthStar, precisionStar) == Format.g);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar) == Format.g);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%La", idx, widthStar, precisionStar) == Format.Lg);
-     assert(idx == 3);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%A", idx, widthStar, precisionStar) == Format.g);
-     assert(idx == 2);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%lg", idx, widthStar, precisionStar) == Format.lg);
-     assert(idx == 3);
-
-     // width, precision
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%*d", idx, widthStar, precisionStar) == Format.d);
-     assert(idx == 3);
-     assert(widthStar && !precisionStar);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%.*d", idx, widthStar, precisionStar) == Format.d);
-     assert(idx == 4);
-     assert(!widthStar && precisionStar);
-
-     idx = 0;
-     assert(parsePrintfFormatSpecifier("%*.*d", idx, widthStar, precisionStar) == Format.d);
-     assert(idx == 5);
-     assert(widthStar && precisionStar);
-
-     // Too short formats
-     {
-         foreach (s; ["%", "%-", "%+", "% ", "%#", "%0", "%*", "%1", "%19", "%.", "%.*", "%.1", "%.12",
-                      "%j", "%z", "%t", "%l", "%h", "%ll", "%hh"])
-         {
-             idx = 0;
-             assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar) == Format.error);
-             assert(idx == s.length);
-         }
-     }
-
-     // Undefined format combinations
-     {
-         foreach (s; ["%#d", "%llg", "%jg", "%zg", "%tg", "%hg", "%hhg",
-                      "%#c", "%0c", "%jc", "%zc", "%tc", "%Lc", "%hc", "%hhc", "%llc",
-                      "%#s", "%0s", "%js", "%zs", "%ts", "%Ls", "%hs", "%hhs", "%lls",
-                      "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp",
-                      "%-n", "%+n", "% n", "%#n", "%0n", "%*n", "%1n", "%19n", "%.n", "%.*n", "%.1n", "%.12n", "%Ln", "%K"])
-         {
-             idx = 0;
-             assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar) == Format.error);
-             assert(idx == s.length);
-         }
-     }
-
-    /* parseScanfFormatSpecifier
-     */
+    size_t idx = 0;
+    bool widthStar;
+    bool precisionStar;
 
-    bool asterisk;
+    void testP(string fmtStr, Format expectedFormat, size_t expectedIdx)
+    {
+        idx = 0;
+        assert(parsePrintfFormatSpecifier(fmtStr, idx, widthStar, precisionStar, useGNUExts) == expectedFormat);
+        assert(idx == expectedIdx);
+    }
 
     // one for each Format
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%d", idx, asterisk) == Format.d);
-    assert(idx == 2);
-    assert(!asterisk);
+    testP("%d", Format.d, 2);
+    assert(!widthStar && !precisionStar);
+
+    testP("%ld", Format.ld, 3);
+    testP("%lld", Format.lld, 4);
+    testP("%jd", Format.jd, 3);
+    testP("%zd", Format.zd, 3);
+    testP("%td", Format.td, 3);
+    testP("%g", Format.g, 2);
+    testP("%Lg", Format.Lg, 3);
+    testP("%p", Format.p, 2);
+    testP("%n", Format.n, 2);
+    testP("%ln", Format.ln, 3);
+    testP("%lln", Format.lln, 4);
+    testP("%hn", Format.hn, 3);
+    testP("%hhn", Format.hhn, 4);
+    testP("%jn", Format.jn, 3);
+    testP("%zn", Format.zn, 3);
+    testP("%tn", Format.tn, 3);
+    testP("%c", Format.c, 2);
+    testP("%lc", Format.lc, 3);
+    testP("%s", Format.s, 2);
+    testP("%ls", Format.ls, 3);
+    testP("%%", Format.percent, 2);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%hhd", idx, asterisk) == Format.hhd);
-    assert(idx == 4);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%hd", idx, asterisk) == Format.hd);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%ld", idx, asterisk) == Format.ld);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%lld", idx, asterisk) == Format.lld);
-    assert(idx == 4);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%jd", idx, asterisk) == Format.jd);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%zd", idx, asterisk) == Format.zd);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%td", idx, asterisk,) == Format.td);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%u", idx, asterisk) == Format.u);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%hhu", idx, asterisk,) == Format.hhu);
-    assert(idx == 4);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%hu", idx, asterisk) == Format.hu);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%lu", idx, asterisk) == Format.lu);
-    assert(idx == 3);
+    // Synonyms
+    testP("%i", Format.d, 2);
+    testP("%u", Format.u, 2);
+    testP("%o", Format.u, 2);
+    testP("%x", Format.u, 2);
+    testP("%X", Format.u, 2);
+    testP("%f", Format.g, 2);
+    testP("%F", Format.g, 2);
+    testP("%G", Format.g, 2);
+    testP("%a", Format.g, 2);
+    testP("%La", Format.Lg, 3);
+    testP("%A", Format.g, 2);
+    testP("%lg", Format.lg, 3);
+
+    // width, precision
+    testP("%*d", Format.d, 3);
+    assert(widthStar && !precisionStar);
+
+    testP("%.*d", Format.d, 4);
+    assert(!widthStar && precisionStar);
+
+    testP("%*.*d", Format.d, 5);
+    assert(widthStar && precisionStar);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%llu", idx, asterisk) == Format.llu);
-    assert(idx == 4);
+    // Too short formats
+    foreach (s; ["%", "%-", "%+", "% ", "%#", "%0", "%*", "%1", "%19", "%.", "%.*", "%.1", "%.12",
+                    "%j", "%z", "%t", "%l", "%h", "%ll", "%hh"])
+    {
+        testP(s, Format.error, s.length);
+    }
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%ju", idx, asterisk) == Format.ju);
-    assert(idx == 3);
+    // Undefined format combinations
+    foreach (s; ["%#d", "%llg", "%jg", "%zg", "%tg", "%hg", "%hhg",
+                    "%#c", "%0c", "%jc", "%zc", "%tc", "%Lc", "%hc", "%hhc", "%llc",
+                    "%#s", "%0s", "%js", "%zs", "%ts", "%Ls", "%hs", "%hhs", "%lls",
+                    "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp",
+                    "%-n", "%+n", "% n", "%#n", "%0n", "%*n", "%1n", "%19n", "%.n", "%.*n", "%.1n", "%.12n", "%Ln", "%K"])
+    {
+        testP(s, Format.error, s.length);
+    }
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%g", idx, asterisk) == Format.g);
-    assert(idx == 2);
+    testP("%C", Format.lc, 2);
+    testP("%S", Format.ls, 2);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%lg", idx, asterisk) == Format.lg);
-    assert(idx == 3);
+    // GNU extensions: explicitly toggle ISO/GNU flag.
+    foreach (s; ["%jm", "%zm", "%tm", "%Lm", "%hm", "%hhm", "%lm", "%llm",
+                    "%#m", "%+m", "%-m", "% m", "%0m"])
+    {
+        useGNUExts = false;
+        testP(s, Format.error, s.length);
+        useGNUExts = true;
+        testP(s, Format.error, s.length);
+    }
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%Lg", idx, asterisk) == Format.Lg);
-    assert(idx == 3);
+    foreach (s; ["%m", "%md", "%mz", "%mc", "%mm", "%msyz", "%ml", "%mlz", "%mlc", "%mlm"])
+    {
+        // valid cases, all parsed as `%m`
+        // GNU printf()
+        useGNUExts = true;
+        testP(s, Format.GNU_m, 2);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%p", idx, asterisk) == Format.p);
-    assert(idx == 2);
+        // ISO printf()
+        useGNUExts = false;
+        testP(s, Format.error, 2);
+    }
+}
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%s", idx, asterisk) == Format.s);
-    assert(idx == 2);
+@("parseScanfFormatSpecifier") unittest
+{
+    size_t idx;
+    bool asterisk;
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%ls", idx, asterisk,) == Format.ls);
-    assert(idx == 3);
+    void testS(string fmtStr, Format expectedFormat, size_t expectedIdx)
+    {
+        idx = 0;
+        assert(parseScanfFormatSpecifier(fmtStr, idx, asterisk) == expectedFormat);
+        assert(idx == expectedIdx);
+    }
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%%", idx, asterisk) == Format.percent);
-    assert(idx == 2);
+    // one for each Format
+    testS("%d", Format.d, 2);
+    testS("%hhd", Format.hhd, 4);
+    testS("%hd", Format.hd, 3);
+    testS("%ld", Format.ld, 3);
+    testS("%lld", Format.lld, 4);
+    testS("%jd", Format.jd, 3);
+    testS("%zd", Format.zd, 3);
+    testS("%td", Format.td, 3);
+    testS("%u", Format.u, 2);
+    testS("%hhu", Format.hhu, 4);
+    testS("%hu", Format.hu, 3);
+    testS("%lu", Format.lu, 3);
+    testS("%llu", Format.llu, 4);
+    testS("%ju", Format.ju, 3);
+    testS("%g", Format.g, 2);
+    testS("%lg", Format.lg, 3);
+    testS("%Lg", Format.Lg, 3);
+    testS("%p", Format.p, 2);
+    testS("%s", Format.s, 2);
+    testS("%ls", Format.ls, 3);
+    testS("%%", Format.percent, 2);
 
     // Synonyms
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%i", idx, asterisk) == Format.d);
-    assert(idx == 2);
+    testS("%i", Format.d, 2);
+    testS("%n", Format.n, 2);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%n", idx, asterisk) == Format.n);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%o", idx, asterisk) == Format.u);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%x", idx, asterisk) == Format.u);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%f", idx, asterisk) == Format.g);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%e", idx, asterisk) == Format.g);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%a", idx, asterisk) == Format.g);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%c", idx, asterisk) == Format.c);
-    assert(idx == 2);
+    testS("%o", Format.u, 2);
+    testS("%x", Format.u, 2);
+    testS("%f", Format.g, 2);
+    testS("%e", Format.g, 2);
+    testS("%a", Format.g, 2);
+    testS("%c", Format.c, 2);
 
     // asterisk
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%*d", idx, asterisk) == Format.d);
-    assert(idx == 3);
+    testS("%*d", Format.d, 3);
     assert(asterisk);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%9ld", idx, asterisk) == Format.ld);
-    assert(idx == 4);
+    testS("%9ld", Format.ld, 4);
     assert(!asterisk);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%*25984hhd", idx, asterisk) == Format.hhd);
-    assert(idx == 10);
+    testS("%*25984hhd", Format.hhd, 10);
     assert(asterisk);
 
     // scansets
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%[a-zA-Z]", idx, asterisk) == Format.s);
-    assert(idx == 9);
+    testS("%[a-zA-Z]", Format.s, 9);
     assert(!asterisk);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%*25l[a-z]", idx, asterisk) == Format.ls);
-    assert(idx == 10);
+    testS("%*25l[a-z]", Format.ls, 10);
     assert(asterisk);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%[]]", idx, asterisk) == Format.s);
-    assert(idx == 4);
+    testS("%[]]", Format.s, 4);
     assert(!asterisk);
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%[^]]", idx, asterisk) == Format.s);
-    assert(idx == 5);
+    testS("%[^]]", Format.s, 5);
     assert(!asterisk);
 
     // Too short formats
     foreach (s; ["%", "% ", "%#", "%0", "%*", "%1", "%19",
                  "%j", "%z", "%t", "%l", "%h", "%ll", "%hh", "%K"])
     {
-        idx = 0;
-        assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
-        assert(idx == s.length);
+
+        testS(s, Format.error, s.length);
     }
 
 
@@ -1468,18 +1292,16 @@ unittest
                  "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp",
                  "%-", "%+", "%#", "%0", "%.", "%Ln"])
     {
-        idx = 0;
-        assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
-        assert(idx == s.length);
+
+        testS(s, Format.error, s.length);
 
     }
 
     // Invalid scansets
     foreach (s; ["%[]", "%[^", "%[^]", "%[s", "%[0-9lld", "%[", "%l[^]"])
     {
-        idx = 0;
-        assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
-        assert(idx == s.length);
+
+        testS(s, Format.error, s.length);
     }
 
     // Posix extensions
@@ -1488,95 +1310,19 @@ unittest
                  "%LC", "%lC", "%llC", "%jC", "%tC", "%hC", "%hhC", "%zC",
                  "%LS", "%lS", "%llS", "%jS", "%tS", "%hS", "%hhS", "%zS"])
     {
-        idx = 0;
-        assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
-        assert(idx == s.length);
-    }
 
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%mc", idx, asterisk) == Format.POSIX_ms);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%ms", idx, asterisk) == Format.POSIX_ms);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%m[0-9]", idx, asterisk) == Format.POSIX_ms);
-    assert(idx == 7);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%mlc", idx, asterisk) == Format.POSIX_mls);
-    assert(idx == 4);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%mls", idx, asterisk) == Format.POSIX_mls);
-    assert(idx == 4);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%ml[^0-9]", idx, asterisk) == Format.POSIX_mls);
-    assert(idx == 9);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%mC", idx, asterisk) == Format.POSIX_mls);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%mS", idx, asterisk) == Format.POSIX_mls);
-    assert(idx == 3);
-
-    idx = 0;
-    assert(parsePrintfFormatSpecifier("%C", idx, widthStar, precisionStar) == Format.lc);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%C", idx, asterisk) == Format.lc);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parsePrintfFormatSpecifier("%S", idx, widthStar, precisionStar) == Format.ls);
-    assert(idx == 2);
-
-    idx = 0;
-    assert(parseScanfFormatSpecifier("%S", idx, asterisk) == Format.ls);
-    assert(idx == 2);
-
-    // GNU extensions: explicitly toggle ISO/GNU flag.
-    // ISO printf()
-    bool useGNUExts = false;
-    {
-        foreach (s; ["%jm", "%zm", "%tm", "%Lm", "%hm", "%hhm", "%lm", "%llm",
-                     "%#m", "%+m", "%-m", "% m", "%0m"])
-        {
-            idx = 0;
-            assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.error);
-            assert(idx == s.length);
-        }
-        foreach (s; ["%m", "%md", "%mz", "%mc", "%mm", "%msyz", "%ml", "%mlz", "%mlc", "%mlm"])
-        {
-            idx = 0;
-            assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.error);
-            assert(idx == 2);
-        }
+        testS(s, Format.error, s.length);
     }
 
-    // GNU printf()
-    useGNUExts = true;
-    {
-        foreach (s; ["%jm", "%zm", "%tm", "%Lm", "%hm", "%hhm", "%lm", "%llm",
-                     "%#m", "%+m", "%-m", "% m", "%0m"])
-        {
-            idx = 0;
-            assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.error);
-            assert(idx == s.length);
-        }
-
-        // valid cases, all parsed as `%m`
-        foreach (s; ["%m", "%md", "%mz", "%mc", "%mm", "%msyz", "%ml", "%mlz", "%mlc", "%mlm"])
-        {
-            idx = 0;
-            assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.GNU_m);
-            assert(idx == 2);
-        }
-    }
+    testS("%mc", Format.POSIX_ms, 3);
+    testS("%ms", Format.POSIX_ms, 3);
+    testS("%m[0-9]", Format.POSIX_ms, 7);
+    testS("%mlc", Format.POSIX_mls, 4);
+    testS("%mls", Format.POSIX_mls, 4);
+    testS("%ml[^0-9]", Format.POSIX_mls, 9);
+    testS("%mC", Format.POSIX_mls, 3);
+    testS("%mS", Format.POSIX_mls, 3);
+
+    testS("%C", Format.lc, 2);
+    testS("%S", Format.ls, 2);
 }
index 1a26eaa434b0b0cd99afa515325727982584aa2f..ba7d590b5530209d7ae9c37c60b866fb5cee9b34 100644 (file)
@@ -105,8 +105,7 @@ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
         scope er = new NullExp(ad.loc, ad.type);    // dummy rvalue
         scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
         el.type = ad.type;
-        Expressions a;
-        a.setDim(1);
+        auto a = Expressions(1);
         const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
         sc = sc.push();
         sc.tinst = null;
@@ -465,8 +464,7 @@ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc)
          */
         scope er = new NullExp(ad.loc, null); // dummy rvalue
         scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
-        Expressions a;
-        a.setDim(1);
+        auto a = Expressions(1);
 
         bool hasIt(Type tthis)
         {
index 2679a63245c7546210e51898957baecebd81b462..2c5a4f0e76ddb91049f56c8bf17199f1d9d51f43 100644 (file)
@@ -1886,15 +1886,6 @@ final class CParser(AST) : Parser!AST
             }
             if (s !is null)
             {
-                s = applySpecifier(s, specifier);
-                if (level == LVL.local)
-                {
-                    // Wrap the declaration in `extern (C) { declaration }`
-                    // Necessary for function pointers, but harmless to apply to all.
-                    auto decls = new AST.Dsymbols(1);
-                    (*decls)[0] = s;
-                    s = new AST.LinkDeclaration(s.loc, linkage, decls);
-                }
                 // Saw `asm("name")` in the function, type, or variable definition.
                 // This is equivalent to `pragma(mangle, "name")` in D
                 if (asmName)
@@ -1917,6 +1908,15 @@ final class CParser(AST) : Parser!AST
                         p.mangleOverride = str;
                     }
                 }
+                s = applySpecifier(s, specifier);
+                if (level == LVL.local)
+                {
+                    // Wrap the declaration in `extern (C) { declaration }`
+                    // Necessary for function pointers, but harmless to apply to all.
+                    auto decls = new AST.Dsymbols(1);
+                    (*decls)[0] = s;
+                    s = new AST.LinkDeclaration(s.loc, linkage, decls);
+                }
                 symbols.push(s);
             }
             first = false;
@@ -2603,7 +2603,6 @@ final class CParser(AST) : Parser!AST
     {
         //printf("cparseDeclarator(%d, %p)\n", declarator, t);
         AST.Types constTypes; // all the Types that will need `const` applied to them
-        constTypes.setDim(0);
 
         AST.Type parseDecl(AST.Type t)
         {
index afd19f3d0f5651ae529cfdec3a2e7dcd0ee9ba00..8ab3873aa33fbd60cd1f1badc5f5a2b1bf689080 100644 (file)
@@ -2979,10 +2979,10 @@ Lagain:
             return Lret(t);
 
         if (t1n.ty == Tvoid) // pointers to void are always compatible
-            return Lret(t2);
+            return Lret(t1);
 
         if (t2n.ty == Tvoid)
-            return Lret(t);
+            return Lret(t2);
 
         if (t1.implicitConvTo(t2))
             return convert(e1, t2);
index bc8db441e60d007ec702c08d1349d28827823d0a..5bce6b0060bbc9e5b8736a0f4d325ffd452aa711 100644 (file)
@@ -210,7 +210,7 @@ public:
     Dsymbol *aliassym;
 
     const char *kind() const override;
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     bool overloadInsert(Dsymbol *s) override;
 
     Dsymbol *toAlias() override;
@@ -625,7 +625,7 @@ public:
     FuncDeclaration *syntaxCopy(Dsymbol *) override;
     bool functionSemantic();
     bool functionSemantic3();
-    bool equals(const RootObject *o) const override final;
+    bool equals(const RootObject * const o) const override final;
 
     int overrides(FuncDeclaration *fd);
     int findVtblIndex(Dsymbols *vtbl, int dim);
index 5cc3772d0526f93052fe698a35a5598502626acf..705acd1c98f2aa232d165f0d390167f36e19edb9 100644 (file)
@@ -265,11 +265,16 @@ extern (C++) final class Import : Dsymbol
             scopesym.addAccessiblePackage(p, visibility);
             foreach (id; packages[1 .. $]) // [b, c]
             {
-                p = cast(Package) p.symtab.lookup(id);
+                auto sym = p.symtab.lookup(id);
                 // https://issues.dlang.org/show_bug.cgi?id=17991
                 // An import of truly empty file/package can happen
                 // https://issues.dlang.org/show_bug.cgi?id=20151
                 // Package in the path conflicts with a module name
+                if (sym is null)
+                    break;
+                // https://issues.dlang.org/show_bug.cgi?id=23327
+                // Package conflicts with symbol of the same name
+                p = sym.isPackage();
                 if (p is null)
                     break;
                 scopesym.addAccessiblePackage(p, visibility);
index a9fd0f56bc034295dede8d0f6cce5629169b7702..a95d9dee88dc89134c4fbd292b8c533ec2450097 100644 (file)
@@ -2830,7 +2830,7 @@ public:
                         (*exps)[i] = ex;
                     }
                 }
-                sd.fill(e.loc, exps, false);
+                sd.fill(e.loc, *exps, false);
 
                 auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, sd, exps, e.newtype);
                 se.origin = se;
@@ -4778,12 +4778,6 @@ public:
             // If `_d_HookTraceImpl` is found, resolve the underlying hook and replace `e` and `fd` with it.
             removeHookTraceImpl(e, fd);
 
-            bool isArrayConstructionOrAssign(FuncDeclaration fd)
-            {
-                return fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor ||
-                fd.ident == Id._d_arrayassign_l || fd.ident == Id._d_arrayassign_r;
-            }
-
             if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor)
             {
                 assert(e.arguments.dim == 1);
@@ -4837,11 +4831,11 @@ public:
                 result = interpretRegion(ae, istate);
                 return;
             }
-            else if (isArrayConstructionOrAssign(fd))
+            else if (isArrayConstructionOrAssign(fd.ident))
             {
                 // In expressionsem.d, the following lowerings were performed:
                 // * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`.
-                // * `ea = eb` (ea and eb are arrays) to `_d_arrayassign_{l,r}(ea[], eb[])`.
+                // * `ea = eb` to `_d_array{,setassign,assign_l,assign_r}(ea[], eb)`.
                 // The following code will rewrite them back to `ea = eb` and
                 // then interpret that expression.
 
index 25794e2c21d1985e9bfa76411dfcb581a5843c63..be0cbccc7c6551b4fc890178933dfc81e797b35b 100644 (file)
@@ -833,6 +833,23 @@ public:
                 printf("  parent = %s %s", s.parent.kind(), s.parent.toChars());
             printf("\n");
         }
+        if (s.parent && s.ident)
+        {
+            if (auto m = s.parent.isModule())
+            {
+                if (m.filetype == FileType.c)
+                {
+                    /* C types at global level get mangled into the __C global namespace
+                     * to get the same mangling regardless of which module it
+                     * is declared in. This works because types are the same if the mangling
+                     * is the same.
+                     */
+                    mangleIdentifier(Id.ImportC, s); // parent
+                    mangleIdentifier(s.ident, s);
+                    return;
+                }
+            }
+        }
         mangleParent(s);
         if (s.ident)
             mangleIdentifier(s.ident, s);
index ba83649da63ee95e39e74be742cec692c7b04e25..e1d589769fe47eb33d05ae35bc44e321289399fb 100644 (file)
@@ -3294,7 +3294,7 @@ private struct MarkdownLink
      * Params:
      *  buf   = an OutBuffer containing the DDoc
      *  i     = the index within `buf` that points to the first character of the URL.
-     *          If this function succeeds `i` will point just after the the end of the URL.
+     *          If this function succeeds `i` will point just after the end of the URL.
      * Returns: whether a URL was found and parsed
      */
     private bool parseHref(ref OutBuffer buf, ref size_t i)
@@ -3362,7 +3362,7 @@ private struct MarkdownLink
      * Params:
      *  buf   = an OutBuffer containing the DDoc
      *  i     = the index within `buf` that points to the first character of the title.
-     *          If this function succeeds `i` will point just after the the end of the title.
+     *          If this function succeeds `i` will point just after the end of the title.
      * Returns: whether a title was found and parsed
      */
     private bool parseTitle(ref OutBuffer buf, ref size_t i)
index c940ff06c67b1ad7986db2ff7b213f9c28861ef7..7e2d02f06bf3ac063a360e8e72c001f75877bbf9 100644 (file)
@@ -1544,6 +1544,12 @@ public:
 
                         if (flags & IgnoreAmbiguous) // if return NULL on ambiguity
                             return null;
+
+                        /* If two imports from C import files, pick first one, as C has global name space
+                         */
+                        if (s.isCsymbol() && s2.isCsymbol())
+                            continue;
+
                         if (!(flags & IgnoreErrors))
                             ScopeDsymbol.multiplyDefined(loc, s, s2);
                         break;
index bea4b776ec5be0f0bf8b250caa3535e88c0be534..acf0004eb80e7525206192b2d8fd517085b820c8 100644 (file)
@@ -189,7 +189,7 @@ public:
     virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments
     Loc getLoc();
     const char *locToChars();
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     bool isAnonymous() const;
     void error(const Loc &loc, const char *format, ...);
     void error(const char *format, ...);
index c3424dc5dfa70770fe8d18a56a6f4e4fe4ebdbff..701f06aadeed3fbae9d7281152f12fb47025730a 100644 (file)
@@ -58,6 +58,7 @@ import dmd.nspace;
 import dmd.objc;
 import dmd.opover;
 import dmd.parse;
+import dmd.root.array;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
@@ -983,7 +984,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 // possibilities.
                 if (fd && !(dsym.storage_class & (STC.manifest | STC.static_ | STC.gshared | STC.extern_)) && !dsym._init.isVoidInitializer())
                 {
-                    //printf("fd = '%s', var = '%s'\n", fd.toChars(), toChars());
+                    //printf("fd = '%s', var = '%s'\n", fd.toChars(), dsym.toChars());
                     if (!ei)
                     {
                         ArrayInitializer ai = dsym._init.isArrayInitializer();
@@ -1014,24 +1015,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                         dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
                     }
 
-                    Expression exp = ei.exp;
-                    Expression e1 = new VarExp(dsym.loc, dsym);
-                    if (isBlit)
-                        exp = new BlitExp(dsym.loc, e1, exp);
-                    else
-                        exp = new ConstructExp(dsym.loc, e1, exp);
-                    dsym.canassign++;
-                    exp = exp.expressionSemantic(sc);
-                    dsym.canassign--;
-                    exp = exp.optimize(WANTvalue);
-                    if (exp.op == EXP.error)
-                    {
-                        dsym._init = new ErrorInitializer();
-                        ei = null;
-                    }
-                    else
-                        ei.exp = exp;
-
                     if (ei && dsym.isScope())
                     {
                         Expression ex = ei.exp.lastComma();
@@ -1054,6 +1037,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                                 f.tookAddressOf--;
                         }
                     }
+
+                    Expression exp = ei.exp;
+                    Expression e1 = new VarExp(dsym.loc, dsym);
+                    if (isBlit)
+                        exp = new BlitExp(dsym.loc, e1, exp);
+                    else
+                        exp = new ConstructExp(dsym.loc, e1, exp);
+                    dsym.canassign++;
+                    exp = exp.expressionSemantic(sc);
+                    dsym.canassign--;
+                    exp = exp.optimize(WANTvalue);
+                    if (exp.op == EXP.error)
+                    {
+                        dsym._init = new ErrorInitializer();
+                        ei = null;
+                    }
+                    else
+                        ei.exp = exp;
                 }
                 else
                 {
@@ -1956,7 +1957,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         //printf("UserAttributeDeclaration::semantic() %p\n", this);
         if (uad.decl && !uad._scope)
             uad.Dsymbol.setScope(sc); // for function local symbols
-        arrayExpressionSemantic(uad.atts, sc, true);
+        arrayExpressionSemantic(uad.atts.peekSlice(), sc, true);
         return attribSemantic(uad);
     }
 
@@ -4182,6 +4183,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             dd.errors = true;
             return;
         }
+
+        if (ad.isClassDeclaration() && ad.classKind == ClassKind.d)
+        {
+            // Class destructors are implicitly `scope`
+            dd.storage_class |= STC.scope_;
+        }
+
         if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
             ad.userDtors.push(dd);
         if (!dd.type)
index 34cae1d14f1f08a48032f6ac3730b6ae7a9274c0..13efc1cba3fee29d842c25659a4fdad5ea7a2e2f 100644 (file)
@@ -1327,7 +1327,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
 
         Loc instLoc = ti.loc;
         Objects* tiargs = ti.tiargs;
-        auto dedargs = new Objects();
+        auto dedargs = new Objects(parameters.dim);
         Objects* dedtypes = &ti.tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T
 
         version (none)
@@ -1346,7 +1346,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
 
         assert(_scope);
 
-        dedargs.setDim(parameters.dim);
         dedargs.zero();
 
         dedtypes.setDim(parameters.dim);
@@ -1511,7 +1510,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
             }
         }
 
-        if (toParent().isModule() || (_scope.stc & STC.static_))
+        if (toParent().isModule())
             tthis = null;
         if (tthis)
         {
@@ -1534,7 +1533,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
             }
 
             // Match attributes of tthis against attributes of fd
-            if (fd.type && !fd.isCtorDeclaration())
+            if (fd.type && !fd.isCtorDeclaration() && !(_scope.stc & STC.static_))
             {
                 StorageClass stc = _scope.stc | fd.storage_class2;
                 // Propagate parent storage class, https://issues.dlang.org/show_bug.cgi?id=5504
@@ -2716,14 +2715,27 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
         if (mfa == MATCH.nomatch)
             return 0;
 
-        if (mfa > m.last) goto LfIsBetter;
-        if (mfa < m.last) goto LlastIsBetter;
+        int firstIsBetter()
+        {
+            td_best = null;
+            ti_best = null;
+            ta_last = MATCH.exact;
+            m.last = mfa;
+            m.lastf = fd;
+            tthis_best = tthis_fd;
+            ov_index = 0;
+            m.count = 1;
+            return 0;
+        }
+
+        if (mfa > m.last) return firstIsBetter();
+        if (mfa < m.last) return 0;
 
         /* See if one of the matches overrides the other.
          */
         assert(m.lastf);
-        if (m.lastf.overrides(fd)) goto LlastIsBetter;
-        if (fd.overrides(m.lastf)) goto LfIsBetter;
+        if (m.lastf.overrides(fd)) return 0;
+        if (fd.overrides(m.lastf)) return firstIsBetter();
 
         /* Try to disambiguate using template-style partial ordering rules.
          * In essence, if f() and g() are ambiguous, if f() can call g(),
@@ -2734,8 +2746,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
             MATCH c1 = fd.leastAsSpecialized(m.lastf);
             MATCH c2 = m.lastf.leastAsSpecialized(fd);
             //printf("c1 = %d, c2 = %d\n", c1, c2);
-            if (c1 > c2) goto LfIsBetter;
-            if (c1 < c2) goto LlastIsBetter;
+            if (c1 > c2) return firstIsBetter();
+            if (c1 < c2) return 0;
         }
 
         /* The 'overrides' check above does covariant checking only
@@ -2756,12 +2768,12 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
             {
                 if (firstCovariant != Covariant.yes && firstCovariant != Covariant.no)
                 {
-                    goto LlastIsBetter;
+                    return 0;
                 }
             }
             else if (firstCovariant == Covariant.yes || firstCovariant == Covariant.no)
             {
-                goto LfIsBetter;
+                return firstIsBetter();
             }
         }
 
@@ -2780,37 +2792,22 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
             fd._linkage == m.lastf._linkage)
         {
             if (fd.fbody && !m.lastf.fbody)
-                goto LfIsBetter;
+                return firstIsBetter();
             if (!fd.fbody)
-                goto LlastIsBetter;
+                return 0;
         }
 
         // https://issues.dlang.org/show_bug.cgi?id=14450
         // Prefer exact qualified constructor for the creating object type
         if (isCtorCall && tf.mod != m.lastf.type.mod)
         {
-            if (tthis.mod == tf.mod) goto LfIsBetter;
-            if (tthis.mod == m.lastf.type.mod) goto LlastIsBetter;
+            if (tthis.mod == tf.mod) return firstIsBetter();
+            if (tthis.mod == m.lastf.type.mod) return 0;
         }
 
         m.nextf = fd;
         m.count++;
         return 0;
-
-    LlastIsBetter:
-        return 0;
-
-    LfIsBetter:
-        td_best = null;
-        ti_best = null;
-        ta_last = MATCH.exact;
-        m.last = mfa;
-        m.lastf = fd;
-        tthis_best = tthis_fd;
-        ov_index = 0;
-        m.count = 1;
-        return 0;
-
     }
 
     int applyTemplate(TemplateDeclaration td)
@@ -3844,10 +3841,20 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
                         tp = (*parameters)[i];
                     else
                     {
+                        Loc loc;
+                        // The "type" (it hasn't been resolved yet) of the function parameter
+                        // does not have a location but the parameter it is related to does,
+                        // so we use that for the resolution (better error message).
+                        if (inferStart < parameters.dim)
+                        {
+                            TemplateParameter loctp = (*parameters)[inferStart];
+                            loc = loctp.loc;
+                        }
+
                         Expression e;
                         Type tx;
                         Dsymbol s;
-                        taa.index.resolve(Loc.initial, sc, e, tx, s);
+                        taa.index.resolve(loc, sc, e, tx, s);
                         edim = s ? getValue(s) : getValue(e);
                     }
                 }
index 4f06bac6593d54e66c28daa4423e79f314ebd697..7ba0a96aa0a350d5dff511235fc36d6bff434e11 100644 (file)
@@ -1423,10 +1423,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
                  *   auto dg = () return { return &x; }
                  * Because dg.ptr points to x, this is returning dt.ptr+offset
                  */
-                if (global.params.useDIP1000 == FeatureState.enabled)
-                {
-                    sc.func.storage_class |= STC.return_ | STC.returninferred;
-                }
+                sc.func.storage_class |= STC.return_ | STC.returninferred;
             }
         }
 
index f871fadead4c4a6ec187a831f64e4813277167f0..42b4dd45c09cca40188a26fdac00c23f5c7f4550 100644 (file)
@@ -7197,6 +7197,26 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag
     }
 }
 
+/**
+ * Verify if the given identifier is any of
+ * _d_array{ctor,setctor,setassign,assign_l, assign_r}.
+ *
+ * Params:
+ *  id = the identifier to verify
+ *
+ * Returns:
+ *  `true` if the identifier corresponds to a construction of assignement
+ *  runtime hook, `false` otherwise.
+ */
+bool isArrayConstructionOrAssign(const Identifier id)
+{
+    import dmd.id : Id;
+
+    return id == Id._d_arrayctor || id == Id._d_arraysetctor ||
+        id == Id._d_arrayassign_l || id == Id._d_arrayassign_r ||
+        id == Id._d_arraysetassign;
+}
+
 /******************************
  * Provide efficient way to implement isUnaExp(), isBinExp(), isBinAssignExp()
  */
index 9ab1cab9877f377dacb270fbdd7b02152f9f554b..c9e3978710687037ff4e471f27b878a35485ae58 100644 (file)
@@ -250,7 +250,7 @@ public:
 
     static IntegerExp *create(const Loc &loc, dinteger_t value, Type *type);
     static void emplace(UnionExp *pue, const Loc &loc, dinteger_t value, Type *type);
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     dinteger_t toInteger() override;
     real_t toReal() override;
     real_t toImaginary() override;
@@ -280,7 +280,7 @@ public:
 
     static RealExp *create(const Loc &loc, real_t value, Type *type);
     static void emplace(UnionExp *pue, const Loc &loc, real_t value, Type *type);
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     dinteger_t toInteger() override;
     uinteger_t toUInteger() override;
     real_t toReal() override;
@@ -297,7 +297,7 @@ public:
 
     static ComplexExp *create(const Loc &loc, complex_t value, Type *type);
     static void emplace(UnionExp *pue, const Loc &loc, complex_t value, Type *type);
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     dinteger_t toInteger() override;
     uinteger_t toUInteger() override;
     real_t toReal() override;
@@ -358,7 +358,7 @@ public:
 class NullExp final : public Expression
 {
 public:
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     Optional<bool> toBool() override;
     StringExp *toStringExp() override;
     void accept(Visitor *v) override { v->visit(this); }
@@ -377,7 +377,7 @@ public:
     static StringExp *create(const Loc &loc, const char *s);
     static StringExp *create(const Loc &loc, const void *s, d_size_t len);
     static void emplace(UnionExp *pue, const Loc &loc, const char *s);
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     char32_t getCodeUnit(d_size_t i) const;
     void setCodeUnit(d_size_t i, char32_t c);
     StringExp *toStringExp() override;
@@ -408,7 +408,7 @@ public:
 
     static TupleExp *create(const Loc &loc, Expressions *exps);
     TupleExp *syntaxCopy() override;
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -423,7 +423,7 @@ public:
     static ArrayLiteralExp *create(const Loc &loc, Expressions *elements);
     static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements);
     ArrayLiteralExp *syntaxCopy() override;
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     Expression *getElement(d_size_t i); // use opIndex instead
     Expression *opIndex(d_size_t i);
     Optional<bool> toBool() override;
@@ -439,7 +439,7 @@ public:
     Expressions *values;
     OwnedBy ownedByCtfe;
 
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     AssocArrayLiteralExp *syntaxCopy() override;
     Optional<bool> toBool() override;
 
@@ -477,7 +477,7 @@ public:
     OwnedBy ownedByCtfe;
 
     static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     StructLiteralExp *syntaxCopy() override;
     Expression *getField(Type *type, unsigned offset);
     int getFieldIndex(Type *type, unsigned offset);
@@ -583,7 +583,7 @@ class VarExp final : public SymbolExp
 public:
     bool delegateWasExtracted;
     static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true);
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     bool isLvalue() override;
     Expression *toLvalue(Scope *sc, Expression *e) override;
     Expression *modifiableLvalue(Scope *sc, Expression *e) override;
@@ -612,7 +612,7 @@ public:
     TemplateDeclaration *td;
     TOK tok;
 
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     FuncExp *syntaxCopy() override;
     const char *toChars() const override;
     bool checkType() override;
index 31141005a6d4a61c434051a792323515d6ac60c4..8a4a13ce1335461a69244707a638d65eba7625be 100644 (file)
@@ -62,6 +62,7 @@ import dmd.opover;
 import dmd.optimize;
 import dmd.parse;
 import dmd.printast;
+import dmd.root.array;
 import dmd.root.ctfloat;
 import dmd.root.file;
 import dmd.root.filename;
@@ -336,22 +337,18 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* p
 /******************************
  * Perform semantic() on an array of Expressions.
  */
-bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors = false)
+extern(D) bool arrayExpressionSemantic(
+    Expression[] exps, Scope* sc, bool preserveErrors = false)
 {
     bool err = false;
-    if (exps)
+    foreach (ref e; exps)
     {
-        foreach (ref e; *exps)
-        {
-            if (e)
-            {
-                auto e2 = e.expressionSemantic(sc);
-                if (e2.op == EXP.error)
-                    err = true;
-                if (preserveErrors || e2.op != EXP.error)
-                    e = e2;
-            }
-        }
+        if (e is null) continue;
+        auto e2 = e.expressionSemantic(sc);
+        if (e2.op == EXP.error)
+            err = true;
+        if (preserveErrors || e2.op != EXP.error)
+            e = e2;
     }
     return err;
 }
@@ -443,7 +440,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
     }
 
     if (!s)
-        return ue.e1.type.getProperty(sc, loc, ident, 0);
+        return ue.e1.type.getProperty(sc, loc, ident, 0, ue.e1);
 
     FuncDeclaration f = s.isFuncDeclaration();
     if (f)
@@ -550,7 +547,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce)
                     if (!global.endGagging(errors))
                         return e;
 
-                    if (arrayExpressionSemantic(originalArguments, sc))
+                    if (arrayExpressionSemantic(originalArguments.peekSlice(), sc))
                         return ErrorExp.get();
 
                     /* fall down to UFCS */
@@ -3111,7 +3108,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         if (e.basis)
             e.basis = e.basis.expressionSemantic(sc);
-        if (arrayExpressionSemantic(e.elements, sc) || (e.basis && e.basis.op == EXP.error))
+        if (arrayExpressionSemantic(e.elements.peekSlice(), sc) || (e.basis && e.basis.op == EXP.error))
             return setError();
 
         expandTuples(e.elements);
@@ -3154,8 +3151,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // Run semantic() on each element
-        bool err_keys = arrayExpressionSemantic(e.keys, sc);
-        bool err_vals = arrayExpressionSemantic(e.values, sc);
+        bool err_keys = arrayExpressionSemantic(e.keys.peekSlice(), sc);
+        bool err_vals = arrayExpressionSemantic(e.values.peekSlice(), sc);
         if (err_keys || err_vals)
             return setError();
 
@@ -3201,7 +3198,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return setError();
 
         // run semantic() on each element
-        if (arrayExpressionSemantic(e.elements, sc))
+        if (arrayExpressionSemantic(e.elements.peekSlice(), sc))
             return setError();
 
         expandTuples(e.elements);
@@ -3213,7 +3210,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         /* Fill out remainder of elements[] with default initializers for fields[]
          */
-        if (!e.sd.fill(e.loc, e.elements, false))
+        if (!e.sd.fill(e.loc, *e.elements, false))
         {
             /* An error in the initializer needs to be recorded as an error
              * in the enclosing function or template, since the initializer
@@ -3524,7 +3521,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         exp.newtype = exp.type; // in case type gets cast to something else
         Type tb = exp.type.toBasetype();
         //printf("tb: %s, deco = %s\n", tb.toChars(), tb.deco);
-        if (arrayExpressionSemantic(exp.arguments, sc))
+        if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc))
         {
             return setError();
         }
@@ -3672,7 +3669,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
 
-            if (cd.disableNew)
+            if (cd.disableNew && !exp.onstack)
             {
                 exp.error("cannot allocate `class %s` with `new` because it is annotated with `@disable new()`",
                           originalNewtype.toChars());
@@ -3807,7 +3804,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (!sd.fit(exp.loc, sc, exp.arguments, tb))
                     return setError();
 
-                if (!sd.fill(exp.loc, exp.arguments, false))
+                if (!sd.fill(exp.loc, *exp.arguments, false))
                     return setError();
 
                 if (checkFrameAccess(exp.loc, sc, sd, exp.arguments ? exp.arguments.dim : 0))
@@ -4259,7 +4256,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         if (FuncExp fe = exp.e1.isFuncExp())
         {
-            if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
+            if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
+                preFunctionParameters(sc, exp.arguments))
                 return setError();
 
             // Run e1 semantic even if arguments have any errors
@@ -4497,7 +4495,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = exp.e1;
             return;
         }
-        if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
+        if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
+            preFunctionParameters(sc, exp.arguments))
             return setError();
 
         // Check for call operator overload
@@ -4543,7 +4542,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                         goto Lx;
 
                     auto sle = new StructLiteralExp(exp.loc, sd, null, exp.e1.type);
-                    if (!sd.fill(exp.loc, sle.elements, true))
+                    if (!sd.fill(exp.loc, *sle.elements, true))
                         return setError();
                     if (checkFrameAccess(exp.loc, sc, sd, sle.elements.dim))
                         return setError();
@@ -4614,7 +4613,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             {
                 Expression e;
 
-                // Make sure to use the the enum type itself rather than its
+                // Make sure to use the enum type itself rather than its
                 // base type
                 // https://issues.dlang.org/show_bug.cgi?id=16346
                 if (exp.e1.type.ty == Tenum)
@@ -8661,7 +8660,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (sd.isNested())
         {
             auto sle = new StructLiteralExp(loc, sd, null, t);
-            if (!sd.fill(loc, sle.elements, true))
+            if (!sd.fill(loc, *sle.elements, true))
                 return ErrorExp.get();
             if (checkFrameAccess(loc, sc, sd, sle.elements.dim))
                 return ErrorExp.get();
@@ -9991,15 +9990,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     }
 
     /***************************************
-     * Lower AssignExp to `_d_arrayassign_{l,r}` if needed.
+     * Lower AssignExp to `_d_array{setassign,assign_l,assign_r}` if needed.
      *
      * Params:
      *      ae = the AssignExp to be lowered
      *      fromCommaExp = indicates whether `ae` is part of a CommaExp or not,
      *                     so no unnecessary temporay variable is created.
      * Returns:
-     *      a CommaExp contiaining call a to `_d_arrayassign_{l,r}` if needed or
-     *      `ae` otherwise
+     *      a CommaExp contiaining call a to `_d_array{setassign,assign_l,assign_r}`
+     *      if needed or `ae` otherwise
      */
     private Expression lowerArrayAssign(AssignExp ae, bool fromCommaExp = false)
     {
@@ -10007,12 +10006,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (t1b.ty != Tsarray && t1b.ty != Tarray)
             return ae;
 
-        const isArrayAssign =
-            (ae.e1.isSliceExp || ae.e1.type.ty == Tsarray) &&
+        const isArrayAssign = (ae.e1.isSliceExp() || ae.e1.type.ty == Tsarray) &&
             (ae.e2.type.ty == Tsarray || ae.e2.type.ty == Tarray) &&
-            (ae.e1.type.nextOf && ae.e2.type.nextOf && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf));
+            (ae.e1.type.nextOf() && ae.e2.type.nextOf() && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf()));
 
-        if (!isArrayAssign)
+        const isArraySetAssign = (ae.e1.isSliceExp() || ae.e1.type.ty == Tsarray) &&
+            (ae.e1.type.nextOf() && ae.e2.type.implicitConvTo(ae.e1.type.nextOf()));
+
+        if (!isArrayAssign && !isArraySetAssign)
             return ae;
 
         const ts = t1b.nextOf().baseElemOf().isTypeStruct();
@@ -10020,9 +10021,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return ae;
 
         Expression res;
-        auto func = ae.e2.isLvalue || ae.e2.isSliceExp ? Id._d_arrayassign_l : Id._d_arrayassign_r;
+        Identifier func = isArraySetAssign ? Id._d_arraysetassign :
+            ae.e2.isLvalue() || ae.e2.isSliceExp() ? Id._d_arrayassign_l : Id._d_arrayassign_r;
 
-        // Lower to `.object._d_arrayassign_l{r}(e1, e2)``
+        // Lower to `.object._d_array{setassign,assign_l,assign_r}(e1, e2)``
         Expression id = new IdentifierExp(ae.loc, Id.empty);
         id = new DotIdExp(ae.loc, id, Id.object);
         id = new DotIdExp(ae.loc, id, func);
@@ -10032,10 +10034,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             .expressionSemantic(sc));
 
         Expression eValue2, value2 = ae.e2;
-        if (ae.e2.isLvalue)
-            value2 = new CastExp(ae.loc, ae.e2, ae.e2.type.nextOf.arrayOf)
+        if (isArrayAssign && value2.isLvalue())
+            value2 = new CastExp(ae.loc, ae.e2, ae.e2.type.nextOf.arrayOf())
                 .expressionSemantic(sc);
-        else if (!fromCommaExp)
+        else if (!fromCommaExp &&
+            (isArrayAssign || (isArraySetAssign && !value2.isLvalue())))
         {
             // Rvalues from CommaExps were introduced in `visit(AssignExp)`
             // and are temporary variables themselves. Rvalues from trivial
@@ -10044,7 +10047,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // `__assigntmp` will be destroyed together with the array `ae.e1`.
             // When `ae.e2` is a variadic arg array, it is also `scope`, so
             // `__assigntmp` may also be scope.
-            auto vd = copyToTemp(STC.rvalue | STC.nodtor | STC.scope_, "__assigntmp", ae.e2);
+            StorageClass stc = STC.nodtor;
+            if (isArrayAssign)
+                stc |= STC.rvalue | STC.scope_;
+
+            auto vd = copyToTemp(stc, "__assigntmp", ae.e2);
             eValue2 = new DeclarationExp(vd.loc, vd).expressionSemantic(sc);
             value2 = new VarExp(vd.loc, vd).expressionSemantic(sc);
         }
@@ -10052,7 +10059,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         Expression ce = new CallExp(ae.loc, id, arguments);
         res = Expression.combine(eValue2, ce).expressionSemantic(sc);
-        res = Expression.combine(res, ae.e1).expressionSemantic(sc);
+        if (isArrayAssign)
+            res = Expression.combine(res, ae.e1).expressionSemantic(sc);
 
         if (global.params.verbose)
             message("lowered   %s =>\n          %s", ae.toChars(), res.toChars());
index 4c0947415cd17d02996b9975990f1d6214626b3e..bcae282f09fcb71bbfb3c93040f2aa569273df8b 100644 (file)
@@ -3216,11 +3216,12 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
         }
     }
 
-    if (tiargs && arrayObjectIsError(tiargs) ||
-        fargs && arrayObjectIsError(cast(Objects*)fargs))
-    {
+    if (tiargs && arrayObjectIsError(tiargs))
         return null;
-    }
+    if (fargs !is null)
+        foreach (arg; *fargs)
+            if (isError(arg))
+                return null;
 
     MatchAccumulator m;
     functionResolve(m, s, loc, sc, tiargs, tthis, fargs, null);
@@ -3758,9 +3759,9 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
     // backend
     bool deferToObj;
 
-    extern (D) this(const ref Loc loc, const ref Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null)
+    extern (D) this(const ref Loc loc, const ref Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, StorageClass storage_class = STC.undefined_)
     {
-        super(loc, endloc, null, STC.undefined_, type);
+        super(loc, endloc, null, storage_class, type);
         this.ident = id ? id : Id.empty;
         this.tok = tok;
         this.fes = fes;
@@ -3774,7 +3775,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
     {
         //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
         assert(!s);
-        auto f = new FuncLiteralDeclaration(loc, endloc, type.syntaxCopy(), tok, fes, ident);
+        auto f = new FuncLiteralDeclaration(loc, endloc, type.syntaxCopy(), tok, fes, ident, storage_class & STC.auto_);
         f.treq = treq; // don't need to copy
         FuncDeclaration.syntaxCopy(f);
         return f;
@@ -3833,9 +3834,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
             {
                 Expression exp = s.exp;
                 if (exp && !exp.type.equals(tret))
-                {
-                    s.exp = exp.castTo(sc, tret);
-                }
+                    s.exp = exp.implicitCastTo(sc, tret);
             }
         }
 
index 7d4fbc3e45b9dde0c360fb8d1ae3937580ab07fb..7a840ffa585546cff398837b147ea8b7c0d33adc 100644 (file)
@@ -84,13 +84,10 @@ int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
 
             case TOK.string_:
                 constraint = p.parsePrimaryExp();
-                // @@@DEPRECATED_2.101@@@
-                // Old parser allowed omitting parentheses around the expression.
-                // Deprecated in 2.091. Can be made permanent error after 2.100
                 if (p.token.value != TOK.leftParenthesis)
                 {
                     arg = p.parseAssignExp();
-                    deprecation(arg.loc, "`%s` must be surrounded by parentheses", arg.toChars());
+                    error(arg.loc, "`%s` must be surrounded by parentheses", arg.toChars());
                 }
                 else
                 {
@@ -527,6 +524,9 @@ unittest
         // Found ',' when expecting ':'
         q{ asm { "", "";
         } },
+
+        // https://issues.dlang.org/show_bug.cgi?id=20593
+        q{ asm { "instruction" : : "operand" 123; } },
     ];
 
     foreach (test; passAsmTests)
index 6695faa5754c2fec766fa6947745b56116d8615d..48ca7665eaef365602a5a67dd6ff1f50c231ed84 100644 (file)
@@ -319,6 +319,7 @@ immutable Msgtable[] msgtable =
     { "_aaApply2" },
     { "_d_arrayctor" },
     { "_d_arraysetctor" },
+    { "_d_arraysetassign" },
     { "_d_arrayassign_l" },
     { "_d_arrayassign_r" },
 
@@ -511,6 +512,7 @@ immutable Msgtable[] msgtable =
     { "wchar_t" },
 
     // for C compiler
+    { "ImportC", "__C" },
     { "__tag" },
     { "dllimport" },
     { "dllexport" },
index 164a5f3e56b0c720e090e6251059bbf9a51979b9..523b5b820d759b53df147cf90ddd632901dc15be 100644 (file)
@@ -167,6 +167,7 @@ extern (C++) final class ArrayInitializer : Initializer
     uint dim;               // length of array being initialized
     Type type;              // type that array will be used to initialize
     bool sem;               // true if semantic() is run
+    bool isCarray;          // C array semantics
 
     extern (D) this(const ref Loc loc)
     {
index 296c31d1a5e1d4f6d2b9d8f8d23eea463c6bc564..977157f5c41434764501edd70f7c4b983ad6bf34 100644 (file)
@@ -78,6 +78,7 @@ public:
     unsigned dim;       // length of array being initialized
     Type *type;         // type that array will be used to initialize
     bool sem;           // true if semantic() is run
+    bool isCarray;      // C array semantics
 
     bool isAssociativeArray() const;
     Expression *toAssocArrayLiteral();
index a57671271048a6a10bd508e29f1ebc48eff0fa4d..ef39f594d88b5d7e28480d515a7f5ede64a9c002 100644 (file)
@@ -225,7 +225,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                 assert(sc);
                 auto tm = vd.type.addMod(t.mod);
                 auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
-                auto ex = iz.initializerToExpression();
+                auto ex = iz.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0);
                 if (ex.op == EXP.error)
                 {
                     errors = true;
@@ -243,7 +243,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
 
             // Make a StructLiteralExp out of elements[]
             auto sle = new StructLiteralExp(i.loc, sd, elements, t);
-            if (!sd.fill(i.loc, elements, false))
+            if (!sd.fill(i.loc, *elements, false))
                 return err();
             sle.type = t;
             auto ie = new ExpInitializer(i.loc, sle);
@@ -272,7 +272,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         uint length;
         const(uint) amax = 0x80000000;
         bool errors = false;
-        //printf("ArrayInitializer::semantic(%s)\n", t.toChars());
+        //printf("ArrayInitializer::semantic(%s), ai: %s %p\n", t.toChars(), i.toChars(), i);
         if (i.sem) // if semantic() already run
         {
             return i;
@@ -374,11 +374,22 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         }
         if (auto tsa = t.isTypeSArray())
         {
-            uinteger_t edim = tsa.dim.toInteger();
-            if (i.dim > edim && !(tsa.isIncomplete() && (sc.flags & SCOPE.Cfile)))
+            if (sc.flags & SCOPE.Cfile && tsa.isIncomplete())
             {
-                error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
-                return err();
+                // Change to array of known length
+                auto tn = tsa.next.toBasetype();
+                tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, i.dim, Type.tsize_t));
+                tx = tsa;      // rewrite caller's type
+                i.type = tsa;  // remember for later passes
+            }
+            else
+            {
+                uinteger_t edim = tsa.dim.toInteger();
+                if (i.dim > edim)
+                {
+                    error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
+                    return err();
+                }
             }
         }
         if (errors)
@@ -394,6 +405,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             error(i.loc, "array dimension %llu exceeds max of %llu", ulong(i.dim), ulong(amax / sz));
             return err();
         }
+        //printf("returns ai: %s\n", i.toChars());
         return i;
     }
 
@@ -661,295 +673,380 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
 
     Initializer visitC(CInitializer ci)
     {
-        if (ci.sem) // if semantic() already run
-            return ci;
         //printf("CInitializer::semantic() (%s) %s\n", t.toChars(), ci.toChars());
-        ci.sem = true;
+        /* Rewrite CInitializer into ExpInitializer, ArrayInitializer, or StructInitializer
+         */
         t = t.toBasetype();
-        ci.type = t;    // later passes will need this
-
-        auto dil = ci.initializerList[];
-        size_t i = 0;   // index into dil[]
-        const uint amax = 0x8000_0000;
-        bool errors;
 
         /* If `{ expression }` return the expression initializer
          */
         ExpInitializer isBraceExpression()
         {
+            auto dil = ci.initializerList[];
             return (dil.length == 1 && !dil[0].designatorList)
                     ? dil[0].initializer.isExpInitializer()
                     : null;
         }
 
-        /* Convert struct initializer into ExpInitializer
+        /********************************
          */
-        Initializer structs(TypeStruct ts)
+        bool overlaps(VarDeclaration field, VarDeclaration[] fields, StructInitializer si)
         {
-            //printf("structs %s\n", ts.toChars());
+            foreach (fld; fields)
+            {
+                if (field.isOverlappedWith(fld))
+                {
+                    // look for initializer corresponding with fld
+                    foreach (i, ident; si.field[])
+                    {
+                        if (ident == fld.ident && si.value[i])
+                            return true;   // already an initializer for `field`
+                    }
+                }
+            }
+            return false;
+        }
+
+        /* Run semantic on ExpInitializer, see if it represents entire struct ts
+         */
+        bool representsStruct(ExpInitializer ei, TypeStruct ts)
+        {
+            if (needInterpret)
+                sc = sc.startCTFE();
+            ei.exp = ei.exp.expressionSemantic(sc);
+            ei.exp = resolveProperties(sc, ei.exp);
+            if (needInterpret)
+                sc = sc.endCTFE();
+            return ei.exp.implicitConvTo(ts) != MATCH.nomatch; // initializer represents the entire struct
+        }
+
+        /* If { } are omitted from substructs, use recursion to reconstruct where
+         * brackets go
+         * Params:
+         *  ts = substruct to initialize
+         *  index = index into ci.initializer, updated
+         * Returns: struct initializer for this substruct
+         */
+        Initializer subStruct()(TypeStruct ts, ref size_t index)
+        {
+            //printf("subStruct(ts: %s, index %d)\n", ts.toChars(), cast(int)index);
+
+            auto si = new StructInitializer(ci.loc);
             StructDeclaration sd = ts.sym;
             sd.size(ci.loc);
             if (sd.sizeok != Sizeok.done)
             {
-                errors = true;
+                index = ci.initializerList.length;
                 return err();
             }
-            const nfields = sd.nonHiddenFields();
-            auto elements = new Expressions(nfields);
-            auto elems = (*elements)[];
-            foreach (ref elem; elems)
-                elem = null;
+            const nfields = sd.fields.length;
 
-          FieldLoop:
-            for (size_t fieldi = 0; fieldi < nfields; ++fieldi)
+            foreach (fieldi; 0 .. nfields)
             {
-                if (i == dil.length)
-                    break;
-
-                auto di = dil[i];
-                if (di.designatorList)
+                if (index >= ci.initializerList.length)
+                    break;          // ran out of initializers
+                auto di = ci.initializerList[index];
+                if (di.designatorList && fieldi != 0)
+                    break;          // back to top level
+                else
                 {
-                    error(ci.loc, "C designator-list not supported yet");
-                    errors = true;
-                    break;
+                    VarDeclaration field;
+                    while (1)   // skip field if it overlaps with previously seen fields
+                    {
+                        field = sd.fields[fieldi];
+                        ++fieldi;
+                        if (!overlaps(field, sd.fields[], si))
+                            break;
+                        if (fieldi == nfields)
+                            break;
+                    }
+                    auto tn = field.type.toBasetype();
+                    auto tnsa = tn.isTypeSArray();
+                    auto tns = tn.isTypeStruct();
+                    auto ix = di.initializer;
+                    if (tnsa && ix.isExpInitializer())
+                    {
+                        ExpInitializer ei = ix.isExpInitializer();
+                        if (ei.exp.isStringExp() && tnsa.nextOf().isintegral())
+                        {
+                            si.addInit(field.ident, ei);
+                            ++index;
+                        }
+                        else
+                            si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template
+                    }
+                    else if (tns && ix.isExpInitializer())
+                    {
+                        /* Disambiguate between an exp representing the entire
+                         * struct, and an exp representing the first field of the struct
+                         */
+                        if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
+                        {
+                            si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret));
+                            ++index;
+                        }
+                        else                                // field initializers for struct
+                            si.addInit(field.ident, subStruct(tns, index)); // the first field
+                    }
+                    else
+                    {
+                        si.addInit(field.ident, ix);
+                        ++index;
+                    }
                 }
+            }
+            //printf("subStruct() returns ai: %s, index: %d\n", si.toChars(), cast(int)index);
+            return si;
+        }
 
-                VarDeclaration vd = sd.fields[fieldi];
+        /* If { } are omitted from subarrays, use recursion to reconstruct where
+         * brackets go
+         * Params:
+         *  tsa = subarray to initialize
+         *  index = index into ci.initializer, updated
+         * Returns: array initializer for this subarray
+         */
+        Initializer subArray(TypeSArray tsa, ref size_t index)
+        {
+            //printf("array(tsa: %s, index %d)\n", tsa.toChars(), cast(int)index);
+            if (tsa.isIncomplete())
+            {
+                // C11 6.2.5-20 "element type shall be complete whenever the array type is specified"
+                assert(0); // should have been detected by parser
+            }
 
-                // Check for overlapping initializations (can happen with unions)
-                foreach (k, v2; sd.fields[0 .. nfields])
+            auto tnsa = tsa.nextOf().toBasetype().isTypeSArray();
+
+            auto ai = new ArrayInitializer(ci.loc);
+            ai.isCarray = true;
+
+            foreach (n; 0 .. cast(size_t)tsa.dim.toInteger())
+            {
+                if (index >= ci.initializerList.length)
+                    break;          // ran out of initializers
+                auto di = ci.initializerList[index];
+                if (di.designatorList)
+                    break;          // back to top level
+                else if (tnsa && di.initializer.isExpInitializer())
                 {
-                    if (vd.isOverlappedWith(v2) && elems[k])
+                    ExpInitializer ei = di.initializer.isExpInitializer();
+                    if (ei.exp.isStringExp() && tnsa.nextOf().isintegral())
                     {
-                        continue FieldLoop;     // skip it
+                        ai.addInit(null, ei);
+                        ++index;
                     }
+                    else
+                        ai.addInit(null, subArray(tnsa, index));
                 }
-
-                ++i;
-
-                // Convert initializer to Expression `ex`
-                assert(sc);
-                auto tm = vd.type.addMod(ts.mod);
-                auto iz = di.initializer.initializerSemantic(sc, tm, needInterpret);
-                auto ex = iz.initializerToExpression(null, true);
-                if (ex.op == EXP.error)
+                else
                 {
-                    errors = true;
-                    continue;
+                    ai.addInit(null, di.initializer);
+                    ++index;
                 }
-
-                elems[fieldi] = ex;
             }
-            if (errors)
-                return err();
-
-            // Make a StructLiteralExp out of elements[]
-            Type tx = ts;
-            auto sle = new StructLiteralExp(ci.loc, sd, elements, tx);
-            if (!sd.fill(ci.loc, elements, false))
-                return err();
-            sle.type = tx;
-            auto ie = new ExpInitializer(ci.loc, sle);
-            return ie.initializerSemantic(sc, tx, needInterpret);
+            //printf("array() returns ai: %s, index: %d\n", ai.toChars(), cast(int)index);
+            return ai;
         }
 
         if (auto ts = t.isTypeStruct())
         {
-            auto ei = structs(ts);
-            if (errors)
-                return err();
-            if (i < dil.length)
+            auto si = new StructInitializer(ci.loc);
+            StructDeclaration sd = ts.sym;
+            sd.size(ci.loc);            // run semantic() on sd to get fields
+            if (sd.sizeok != Sizeok.done)
             {
-                error(ci.loc, "%d extra initializer(s) for `struct %s`", cast(int)(dil.length - i), ts.toChars());
                 return err();
             }
-            return ei;
-        }
+            const nfields = sd.fields.length;
 
-        auto tsa = t.isTypeSArray();
-        if (!tsa)
-        {
-            /* Not an array. See if it is `{ exp }` which can be
-             * converted to an ExpInitializer
-             */
-            if (ExpInitializer ei = isBraceExpression())
-            {
-                return ei.initializerSemantic(sc, t, needInterpret);
-            }
-
-            error(ci.loc, "C non-array initializer (%s) %s not supported yet", t.toChars(), ci.toChars());
-            return err();
-        }
+            size_t fieldi = 0;
 
-        /* If it's an array of integral being initialized by `{ string }`
-         * replace with `string`
-         */
-        auto tn = t.nextOf();
-        if (tn.isintegral())
-        {
-            if (ExpInitializer ei = isBraceExpression())
+            for (size_t index = 0; index < ci.initializerList.length; )
             {
-                if (ei.exp.isStringExp())
-                    return ei.initializerSemantic(sc, t, needInterpret);
+                auto di = ci.initializerList[index];
+                auto dlist = di.designatorList;
+                if (dlist)
+                {
+                    const length = (*dlist).length;
+                    if (length == 0 || !(*dlist)[0].ident)
+                    {
+                        error(ci.loc, "`.identifier` expected for C struct field initializer `%s`", ci.toChars());
+                        return err();
+                    }
+                    if (length > 1)
+                    {
+                        error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", ci.toChars());
+                        return err();
+                    }
+                    auto id = (*dlist)[0].ident;
+                    foreach (k, f; sd.fields[])         // linear search for now
+                    {
+                        if (f.ident == id)
+                        {
+                            fieldi = k;
+                            si.addInit(id, di.initializer);
+                            ++fieldi;
+                            ++index;
+                            break;
+                        }
+                    }
+                }
+                else
+                {
+                    if (fieldi == nfields)
+                        break;
+                    VarDeclaration field;
+                    while (1)   // skip field if it overlaps with previously seen fields
+                    {
+                        field = sd.fields[fieldi];
+                        ++fieldi;
+                        if (!overlaps(field, sd.fields[], si))
+                            break;
+                        if (fieldi == nfields)
+                            break;
+                    }
+                    auto tn = field.type.toBasetype();
+                    auto tnsa = tn.isTypeSArray();
+                    auto tns = tn.isTypeStruct();
+                    auto ix = di.initializer;
+                    if (tnsa && ix.isExpInitializer())
+                    {
+                        ExpInitializer ei = ix.isExpInitializer();
+                        if (ei.exp.isStringExp() && tnsa.nextOf().isintegral())
+                        {
+                            si.addInit(field.ident, ei);
+                            ++index;
+                        }
+                        else
+                            si.addInit(field.ident, subArray(tnsa, index));
+                    }
+                    else if (tns && ix.isExpInitializer())
+                    {
+                        /* Disambiguate between an exp representing the entire
+                         * struct, and an exp representing the first field of the struct
+                         */
+                        if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
+                        {
+                            si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret));
+                            ++index;
+                        }
+                        else                                // field initializers for struct
+                            si.addInit(field.ident, subStruct(tns, index)); // the first field
+                    }
+                    else
+                    {
+                        si.addInit(field.ident, di.initializer);
+                        ++index;
+                    }
+                }
             }
+            return initializerSemantic(si, sc, t, needInterpret);
         }
-
-        /* Support recursion to handle un-braced array initializers
-         * Params:
-         *    t = element type
-         *    dim = max number of elements
-         *    simple = true if array of simple elements
-         * Returns:
-         *    # of elements in array
-         */
-        size_t array(Type t, size_t dim, ref bool simple)
+        else if (auto ta = t.isTypeSArray())
         {
-            //printf(" type %s i %d dim %d dil.length = %d\n", t.toChars(), cast(int)i, cast(int)dim, cast(int)dil.length);
-            auto tn = t.nextOf().toBasetype();
-            auto tnsa = tn.isTypeSArray();
-            if (tnsa && tnsa.isIncomplete())
-            {
-                // C11 6.2.5-20 "element type shall be complete whenever the array type is specified"
-                error(ci.loc, "incomplete element type `%s` not allowed", tnsa.toChars());
-                errors = true;
-                return 1;
-            }
-            if (i == dil.length)
-                return 0;
-            size_t n;
-            const nelems = tnsa ? cast(size_t)tnsa.dim.toInteger() : 0;
+            auto tn = t.nextOf().toBasetype();  // element type of array
 
-            /* Run initializerSemantic on a single element.
+            /* If it's an array of integral being initialized by `{ string }`
+             * replace with `string`
              */
-            Initializer elem(Initializer ie)
+            if (tn.isintegral())
             {
-                ++i;
-                auto tnx = tn; // in case initializerSemantic tries to change it
-                ie = ie.initializerSemantic(sc, tnx, needInterpret);
-                if (ie.isErrorInitializer())
-                    errors = true;
-                assert(tnx == tn); // sub-types should not be modified
-                return ie;
+                if (ExpInitializer ei = isBraceExpression())
+                {
+                    if (ei.exp.isStringExp())
+                        return ei.initializerSemantic(sc, t, needInterpret);
+                }
             }
 
-            foreach (j; 0 .. dim)
+            auto tnsa = tn.isTypeSArray();      // array of array
+            auto tns = tn.isTypeStruct();       // array of struct
+
+            auto ai = new ArrayInitializer(ci.loc);
+            ai.isCarray = true;
+            for (size_t index = 0; index < ci.initializerList.length; )
             {
-                auto di = dil[i];
-                if (di.designatorList)
-                {
-                    error(ci.loc, "C designator-list not supported yet");
-                    errors = true;
-                    break;
-                }
-                if (tnsa && di.initializer.isExpInitializer())
+                auto di = ci.initializerList[index];
+                if (auto dlist = di.designatorList)
                 {
-                    // no braces enclosing array initializer, so recurse
-                    array(tnsa, nelems, simple);
-                }
-                else if (auto tns = tn.isTypeStruct())
-                {
-                    if (auto ei = di.initializer.isExpInitializer())
+                    const length = (*dlist).length;
+                    if (length == 0 || !(*dlist)[0].exp)
+                    {
+                        error(ci.loc, "`[ constant-expression ]` expected for C array element initializer `%s`", ci.toChars());
+                        return err();
+                    }
+                    if (length > 1)
+                    {
+                        error(ci.loc, "only 1 designator currently allowed for C array element initializer `%s`", ci.toChars());
+                        return err();
+                    }
+                    //printf("tn: %s, di.initializer: %s\n", tn.toChars(), di.initializer.toChars());
+                    auto ix = di.initializer;
+                    if (tnsa && ix.isExpInitializer())
+                    {
+                        // Wrap initializer in [ ]
+                        auto ain = new ArrayInitializer(ci.loc);
+                        ain.addInit(null, di.initializer);
+                        ix = ain;
+                        ai.addInit((*dlist)[0].exp, initializerSemantic(ix, sc, tn, needInterpret));
+                        ++index;
+                    }
+                    else if (tns && ix.isExpInitializer())
                     {
-                        // no braces enclosing struct initializer
-
                         /* Disambiguate between an exp representing the entire
                          * struct, and an exp representing the first field of the struct
-                        */
-                        if (needInterpret)
-                            sc = sc.startCTFE();
-                        ei.exp = ei.exp.expressionSemantic(sc);
-                        ei.exp = resolveProperties(sc, ei.exp);
-                        if (needInterpret)
-                            sc = sc.endCTFE();
-                        if (ei.exp.implicitConvTo(tn))
-                            di.initializer = elem(di.initializer); // the whole struct
-                        else
+                         */
+                        if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
                         {
-                            simple = false;
-                            dil[n].initializer = structs(tns); // the first field
+                            ai.addInit((*dlist)[0].exp, initializerSemantic(ix, sc, tn, needInterpret));
+                            ++index;
                         }
+                        else                                // field initializers for struct
+                            ai.addInit((*dlist)[0].exp, subStruct(tns, index)); // the first field
+                    }
+                    else
+                    {
+                        ai.addInit((*dlist)[0].exp, initializerSemantic(ix, sc, tn, needInterpret));
+                        ++index;
+                    }
+                }
+                else if (tnsa && di.initializer.isExpInitializer())
+                {
+                    ExpInitializer ei = di.initializer.isExpInitializer();
+                    if (ei.exp.isStringExp() && tnsa.nextOf().isintegral())
+                    {
+                        ai.addInit(null, ei);
+                        ++index;
                     }
                     else
-                        dil[n].initializer = elem(di.initializer);
+                        ai.addInit(null, subArray(tnsa, index));
+                }
+                else if (tns && di.initializer.isExpInitializer())
+                {
+                    /* Disambiguate between an exp representing the entire
+                     * struct, and an exp representing the first field of the struct
+                     */
+                    if (representsStruct(di.initializer.isExpInitializer(), tns)) // initializer represents the entire struct
+                    {
+                        ai.addInit(null, initializerSemantic(di.initializer, sc, tn, needInterpret));
+                        ++index;
+                    }
+                    else                                // field initializers for struct
+                        ai.addInit(null, subStruct(tns, index)); // the first field
                 }
                 else
                 {
-                    di.initializer = elem(di.initializer);
+                    ai.addInit(null, initializerSemantic(di.initializer, sc, tn, needInterpret));
+                    ++index;
                 }
-                ++n;
-                if (i == dil.length)
-                    break;
-            }
-            //printf(" n: %d i: %d\n", cast(int)n, cast(int)i);
-            return n;
-        }
-
-        size_t dim = tsa.isIncomplete() ? dil.length : cast(size_t)tsa.dim.toInteger();
-        bool simple = true;
-        auto newdim = array(t, dim, simple);
-
-        if (errors)
-            return err();
-
-        if (tsa.isIncomplete()) // array of unknown length
-        {
-            // Change to array of known length
-            tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, newdim, Type.tsize_t));
-            tx = tsa;       // rewrite caller's type
-            ci.type = tsa;  // remember for later passes
-        }
-        const uinteger_t edim = tsa.dim.toInteger();
-        if (i < dil.length)
-        {
-            error(ci.loc, "%d extra initializer(s) for static array length of %d", cast(int)(dil.length - i), cast(int)edim);
-            return err();
-        }
-
-        const sz = tn.size(); // element size
-        if (sz == SIZE_INVALID)
-            return err();
-        bool overflow;
-        const max = mulu(edim, sz, overflow);
-        if (overflow || max >= amax)
-        {
-            error(ci.loc, "array dimension %llu exceeds max of %llu", ulong(edim), ulong(amax / sz));
-            return err();
-        }
-
-        /* If an array of simple elements, replace with an ArrayInitializer
-         */
-        auto tnb = tn.toBasetype();
-        if (!tnb.isTypeSArray() && (!tnb.isTypeStruct() || simple))
-        {
-            auto ai = new ArrayInitializer(ci.loc);
-            ai.dim = cast(uint) dil.length;
-            ai.index.setDim(dil.length);
-            ai.value.setDim(dil.length);
-            foreach (const j; 0 .. dil.length)
-            {
-                ai.index[j] = null;
-                ai.value[j] = dil[j].initializer;
             }
-            auto ty = tx;
-            return ai.initializerSemantic(sc, ty, needInterpret);
+            return initializerSemantic(ai, sc, tx, needInterpret);
         }
-
-        if (newdim < ci.initializerList.length && tnb.isTypeStruct())
+        else if (ExpInitializer ei = isBraceExpression())
+            return visitExp(ei);
+        else
         {
-            // https://issues.dlang.org/show_bug.cgi?id=22375
-            // initializerList can be bigger than the number of actual elements
-            // to initialize for array of structs because it is not required
-            // for values to have proper bracing.
-            // i.e: These are all valid initializers for `struct{int a,b;}[3]`:
-            //      {1,2,3,4}, {{1,2},3,4}, {1,2,{3,4}}, {{1,2},{3,4}}
-            // In all examples above, the new length of the initializer list
-            // has been shortened from four elements to two. This is important,
-            // because `dil` is written back to directly, making the lowered
-            // initializer `{{1,2},{3,4}}` and not `{{1,2},{3,4},3,4}`.
-            ci.initializerList.length = newdim;
+            assert(0);
         }
-
-        return ci;
     }
 
     final switch (init.kind)
index 21bbde8227fa1f17248813672af975b0171516f9..1de89d4d6b60e0d0ee25fc45aa4ae3f0a955f5dd 100644 (file)
@@ -2582,8 +2582,13 @@ class Lexer
         {
             /* C11 6.4.4.2 doesn't actually care if it is not representable if it is not hex
              */
-            const char* suffix = (result == TOK.float32Literal || result == TOK.imaginary32Literal) ? "f" : "";
-            error(scanloc, "number `%s%s` is not representable", sbufptr, suffix);
+            const char* suffix = result == TOK.float32Literal ? "f" : result == TOK.float80Literal ? "L" : "";
+            const char* type = [TOK.float32Literal: "`float`".ptr,
+                                TOK.float64Literal: "`double`".ptr,
+                                TOK.float80Literal: "`real` for the current target".ptr][result];
+            error(scanloc, "number `%s%s` is not representable as a %s", sbufptr, suffix, type);
+            const char* extra = result == TOK.float64Literal ? "`real` literals can be written using the `L` suffix. " : "";
+            errorSupplemental(scanloc, "%shttps://dlang.org/spec/lex.html#floatliteral", extra);
         }
         debug
         {
index 6bfb729170c97ee95bfbbd6c2174420be39386b1..341ce361f3cc7f8fce709233c67c8c9aee69a7ae 100644 (file)
@@ -37,7 +37,7 @@ public:
 
     const char *kind() const override;
 
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
 
     Package *isPackage() override final { return this; }
 
index f2da41b6ff7b97df7f41bfce6ee25d6d20aee202..1240f5a8a9d40dd489cd16a5e754abe2e18f4743 100644 (file)
@@ -936,7 +936,7 @@ extern (C++) abstract class Type : ASTNode
         else
         {
             // If `typeSemantic` succeeded, there may have been deprecations that
-            // were gagged due the the `startGagging` above.  Run again to display
+            // were gagged due the `startGagging` above.  Run again to display
             // those deprecations.  https://issues.dlang.org/show_bug.cgi?id=19107
             if (global.gaggedWarnings > 0)
                 typeSemantic(tcopy, loc, sc);
@@ -4656,7 +4656,7 @@ extern (C++) final class TypeFunction : TypeNext
                 // suppress early exit if an error message is wanted,
                 // so we can check any matching args are valid
                 if (!pMessage)
-                    goto Nomatch;
+                    return MATCH.nomatch;
             }
             // too many args; no match
             match = MATCH.convert; // match ... with a "conversion" match level
@@ -4669,7 +4669,7 @@ extern (C++) final class TypeFunction : TypeNext
             buf.printf("too few arguments, expected `%d`, got `%d`", cast(int)nparams, cast(int)nargs);
             if (pMessage)
                 *pMessage = buf.extractChars();
-            goto Nomatch;
+            return MATCH.nomatch;
         }
 
         foreach (u, p; parameterList)
@@ -4710,226 +4710,16 @@ extern (C++) final class TypeFunction : TypeNext
             MATCH m;
 
             assert(p);
-            if (u >= nargs)
-            {
-                if (p.defaultArg)
-                    continue;
-                // try typesafe variadics
-                goto L1;
-            }
+
+            // One or more arguments remain
+            if (u < nargs)
             {
                 Expression arg = args[u];
                 assert(arg);
-                //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars());
-
-                Type targ = arg.type;
-                Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type;
-
-                if (p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid)
-                    m = MATCH.convert;
-                else
-                {
-                    //printf("%s of type %s implicitConvTo %s\n", arg.toChars(), targ.toChars(), tprm.toChars());
-                    if (flag)
-                    {
-                        // for partial ordering, value is an irrelevant mockup, just look at the type
-                        m = targ.implicitConvTo(tprm);
-                    }
-                    else
-                    {
-                        const isRef = p.isReference();
-
-                        StructDeclaration argStruct, prmStruct;
-
-                        // first look for a copy constructor
-                        if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct)
-                        {
-                            // if the argument and the parameter are of the same unqualified struct type
-                            argStruct = (cast(TypeStruct)targ).sym;
-                            prmStruct = (cast(TypeStruct)tprm).sym;
-                        }
-
-                        // check if the copy constructor may be called to copy the argument
-                        if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
-                        {
-                            /* this is done by seeing if a call to the copy constructor can be made:
-                             *
-                             * typeof(tprm) __copytmp;
-                             * copytmp.__copyCtor(arg);
-                             */
-                            auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null);
-                            tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe;
-                            tmp.dsymbolSemantic(sc);
-                            Expression ve = new VarExp(arg.loc, tmp);
-                            Expression e = new DotIdExp(arg.loc, ve, Id.ctor);
-                            e = new CallExp(arg.loc, e, arg);
-                            //printf("e = %s\n", e.toChars());
-                            if(.trySemantic(e, sc))
-                                m = MATCH.exact;
-                            else
-                            {
-                                if (pMessage)
-                                {
-                                    /* https://issues.dlang.org/show_bug.cgi?id=22202
-                                     *
-                                     * If a function was deduced by semantic on the CallExp,
-                                     * it means that resolveFuncCall completed succesfully.
-                                     * Therefore, there exists a callable copy constructor,
-                                     * however, it cannot be called because scope constraints
-                                     * such as purity, safety or nogc.
-                                     */
-                                    OutBuffer buf;
-                                    auto callExp = e.isCallExp();
-                                    if (auto f = callExp.f)
-                                    {
-                                        char[] s;
-                                        if (!f.isPure && sc.func.setImpure())
-                                            s ~= "pure ";
-                                        if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe())
-                                            s ~= "@safe ";
-                                        if (!f.isNogc && sc.func.setGC())
-                                            s ~= "nogc ";
-                                        if (s)
-                                        {
-                                            s[$-1] = '\0';
-                                            buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr);
-                                        }
-                                        else if (f.isGenerated() && f.isDisabled())
-                                        {
-                                            /* https://issues.dlang.org/show_bug.cgi?id=23097
-                                             * Compiler generated copy constructor failed.
-                                             */
-                                            buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable",
-                                                       argStruct.toChars());
-                                        }
-                                        else
-                                        {
-                                            /* Although a copy constructor may exist, no suitable match was found.
-                                             * i.e: `inout` constructor creates `const` object, not mutable.
-                                             * Fallback to using the original generic error before bugzilla 22202.
-                                             */
-                                            goto Lnocpctor;
-                                        }
-                                    }
-                                    else
-                                    {
-                                    Lnocpctor:
-                                        buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies",
-                                               argStruct.toChars(), targ.toChars(), tprm.toChars());
-                                    }
-
-                                    *pMessage = buf.extractChars();
-                                }
-                                m = MATCH.nomatch;
-                                goto Nomatch;
-                            }
-                        }
-                        else
-                        {
-                            import dmd.dcast : cimplicitConvTo;
-                            m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm);
-                        }
-                    }
-                    //printf("match %d\n", m);
-                }
-
-                // Non-lvalues do not match ref or out parameters
-                if (p.isReference())
-                {
-                    // https://issues.dlang.org/show_bug.cgi?id=13783
-                    // Don't use toBasetype() to handle enum types.
-                    Type ta = targ;
-                    Type tp = tprm;
-                    //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars());
-
-                    if (m && !arg.isLvalue())
-                    {
-                        if (p.storageClass & STC.out_)
-                        {
-                            if (pMessage) *pMessage = getParamError(arg, p);
-                            goto Nomatch;
-                        }
-
-                        if (arg.op == EXP.string_ && tp.ty == Tsarray)
-                        {
-                            if (ta.ty != Tsarray)
-                            {
-                                Type tn = tp.nextOf().castMod(ta.nextOf().mod);
-                                dinteger_t dim = (cast(StringExp)arg).len;
-                                ta = tn.sarrayOf(dim);
-                            }
-                        }
-                        else if (arg.op == EXP.slice && tp.ty == Tsarray)
-                        {
-                            // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
-                            if (ta.ty != Tsarray)
-                            {
-                                Type tn = ta.nextOf();
-                                dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
-                                ta = tn.sarrayOf(dim);
-                            }
-                        }
-                        else if ((p.storageClass & STC.in_) && global.params.previewIn)
-                        {
-                            // Allow converting a literal to an `in` which is `ref`
-                            if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray)
-                            {
-                                Type tn = tp.nextOf();
-                                dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
-                                ta = tn.sarrayOf(dim);
-                            }
-
-                            // Need to make this a rvalue through a temporary
-                            m = MATCH.convert;
-                        }
-                        else if (global.params.rvalueRefParam != FeatureState.enabled ||
-                                 p.storageClass & STC.out_ ||
-                                 !arg.type.isCopyable())  // can't copy to temp for ref parameter
-                        {
-                            if (pMessage) *pMessage = getParamError(arg, p);
-                            goto Nomatch;
-                        }
-                        else
-                        {
-                            /* in functionParameters() we'll convert this
-                             * rvalue into a temporary
-                             */
-                            m = MATCH.convert;
-                        }
-                    }
-
-                    /* If the match is not already perfect or if the arg
-                       is not a lvalue then try the `alias this` chain
-                       see  https://issues.dlang.org/show_bug.cgi?id=15674
-                       and https://issues.dlang.org/show_bug.cgi?id=21905
-                    */
-                    if (ta != tp || !arg.isLvalue())
-                    {
-                        Type firsttab = ta.toBasetype();
-                        while (1)
-                        {
-                            Type tab = ta.toBasetype();
-                            Type tat = tab.aliasthisOf();
-                            if (!tat || !tat.implicitConvTo(tprm))
-                                break;
-                            if (tat == tab || tat == firsttab)
-                                break;
-                            ta = tat;
-                        }
-                    }
-
-                    /* A ref variable should work like a head-const reference.
-                     * e.g. disallows:
-                     *  ref T      <- an lvalue of const(T) argument
-                     *  ref T[dim] <- an lvalue of const(T[dim]) argument
-                     */
-                    if (!ta.constConv(tp))
-                    {
-                        if (pMessage) *pMessage = getParamError(arg, p);
-                        goto Nomatch;
-                    }
-                }
+                m = argumentMatchParameter(this, p, arg, wildmatch, flag, sc, pMessage);
             }
+            else if (p.defaultArg)
+                continue;
 
             /* prefer matching the element type rather than the array
              * type when more arguments are present with T[]...
@@ -4943,100 +4733,33 @@ extern (C++) final class TypeFunction : TypeNext
             L1:
                 if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param
                 {
-                    Type tb = p.type.toBasetype();
-                    TypeSArray tsa;
-                    dinteger_t sz;
-
-                    switch (tb.ty)
-                    {
-                    case Tsarray:
-                        tsa = cast(TypeSArray)tb;
-                        sz = tsa.dim.toInteger();
-                        if (sz != nargs - u)
-                        {
-                            if (pMessage)
-                                // Windows (Vista) OutBuffer.vprintf issue? 2nd argument always zero
-                                //*pMessage = getMatchError("expected %d variadic argument(s), not %d", sz, nargs - u);
-                            if (!global.gag || global.params.showGaggedErrors)
-                            {
-                                OutBuffer buf;
-                                buf.printf("expected %llu variadic argument(s)", sz);
-                                buf.printf(", not %zu", nargs - u);
-                                *pMessage = buf.extractChars();
-                            }
-                            goto Nomatch;
-                        }
-                        goto case Tarray;
-                    case Tarray:
-                        {
-                            TypeArray ta = cast(TypeArray)tb;
-                            foreach (arg; args[u .. nargs])
-                            {
-                                assert(arg);
-
-                                /* If lazy array of delegates,
-                                 * convert arg(s) to delegate(s)
-                                 */
-                                Type tret = p.isLazyArray();
-                                if (tret)
-                                {
-                                    if (ta.next.equals(arg.type))
-                                        m = MATCH.exact;
-                                    else if (tret.toBasetype().ty == Tvoid)
-                                        m = MATCH.convert;
-                                    else
-                                    {
-                                        m = arg.implicitConvTo(tret);
-                                        if (m == MATCH.nomatch)
-                                            m = arg.implicitConvTo(ta.next);
-                                    }
-                                }
-                                else
-                                    m = arg.implicitConvTo(ta.next);
-
-                                if (m == MATCH.nomatch)
-                                {
-                                    if (pMessage) *pMessage = getParamError(arg, p);
-                                    goto Nomatch;
-                                }
-                                if (m < match)
-                                    match = m;
-                            }
-                            goto Ldone;
-                        }
-                    case Tclass:
-                        // Should see if there's a constructor match?
-                        // Or just leave it ambiguous?
-                        goto Ldone;
-
-                    default:
-                        break;
-                    }
+                    auto trailingArgs = args[u .. $];
+                    if (auto vmatch = matchTypeSafeVarArgs(this, p, trailingArgs, pMessage))
+                        return vmatch < match ? vmatch : match;
+                    // Error message was already generated in `matchTypeSafeVarArgs`
+                    return MATCH.nomatch;
                 }
-                if (pMessage && u < nargs)
-                    *pMessage = getParamError(args[u], p);
-                else if (pMessage)
+                if (pMessage && u >= nargs)
                     *pMessage = getMatchError("missing argument for parameter #%d: `%s`",
                         u + 1, parameterToChars(p, this, false));
-                goto Nomatch;
+                // If an error happened previously, `pMessage` was already filled
+                else if (pMessage && !*pMessage)
+                    *pMessage = getParamError(args[u], p);
+
+                return MATCH.nomatch;
             }
             if (m < match)
                 match = m; // pick worst match
         }
 
-    Ldone:
         if (pMessage && !parameterList.varargs && nargs > nparams)
         {
             // all parameters had a match, but there are surplus args
             *pMessage = getMatchError("expected %d argument(s), not %d", nparams, nargs);
-            goto Nomatch;
+            return MATCH.nomatch;
         }
         //printf("match = %d\n", match);
         return match;
-
-    Nomatch:
-        //printf("no match\n");
-        return MATCH.nomatch;
     }
 
     /+
@@ -6194,6 +5917,11 @@ extern (C++) final class TypeClass : Type
         if (t && t.ty == Tclass)
         {
             ClassDeclaration cd = (cast(TypeClass)t).sym;
+            if (cd.semanticRun < PASS.semanticdone && !cd.isBaseInfoComplete())
+                cd.dsymbolSemantic(null);
+            if (sym.semanticRun < PASS.semanticdone && !sym.isBaseInfoComplete())
+                sym.dsymbolSemantic(null);
+
             if (sym.isBaseOf(cd, poffset))
                 return true;
         }
@@ -6355,10 +6083,9 @@ extern (C++) final class TypeTuple : Type
     extern (D) this(Expressions* exps)
     {
         super(Ttuple);
-        auto arguments = new Parameters();
+        auto arguments = new Parameters(exps ? exps.dim : 0);
         if (exps)
         {
-            arguments.setDim(exps.dim);
             for (size_t i = 0; i < exps.dim; i++)
             {
                 Expression e = (*exps)[i];
@@ -7330,3 +7057,325 @@ const(char)* toChars(ScopeRef sr) pure nothrow @nogc @safe
         return names[sr];
     }
 }
+
+/**
+ * Used by `callMatch` to check if the copy constructor may be called to
+ * copy the argument
+ *
+ * This is done by seeing if a call to the copy constructor can be made:
+ * ```
+ * typeof(tprm) __copytmp;
+ * copytmp.__copyCtor(arg);
+ * ```
+ */
+private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
+    Expression arg, Type tprm, Scope* sc, const(char)** pMessage)
+{
+    auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null);
+    tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe;
+    tmp.dsymbolSemantic(sc);
+    Expression ve = new VarExp(arg.loc, tmp);
+    Expression e = new DotIdExp(arg.loc, ve, Id.ctor);
+    e = new CallExp(arg.loc, e, arg);
+    //printf("e = %s\n", e.toChars());
+    if (.trySemantic(e, sc))
+        return true;
+
+    if (pMessage)
+    {
+        /* https://issues.dlang.org/show_bug.cgi?id=22202
+         *
+         * If a function was deduced by semantic on the CallExp,
+         * it means that resolveFuncCall completed succesfully.
+         * Therefore, there exists a callable copy constructor,
+         * however, it cannot be called because scope constraints
+         * such as purity, safety or nogc.
+         */
+        OutBuffer buf;
+        auto callExp = e.isCallExp();
+        if (auto f = callExp.f)
+        {
+            char[] s;
+            if (!f.isPure && sc.func.setImpure())
+                s ~= "pure ";
+            if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe())
+                s ~= "@safe ";
+            if (!f.isNogc && sc.func.setGC())
+                s ~= "nogc ";
+            if (s)
+            {
+                s[$-1] = '\0';
+                buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr);
+            }
+            else if (f.isGenerated() && f.isDisabled())
+            {
+                /* https://issues.dlang.org/show_bug.cgi?id=23097
+                 * Compiler generated copy constructor failed.
+                 */
+                buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable",
+                           argStruct.toChars());
+            }
+            else
+            {
+                /* Although a copy constructor may exist, no suitable match was found.
+                 * i.e: `inout` constructor creates `const` object, not mutable.
+                 * Fallback to using the original generic error before bugzilla 22202.
+                 */
+                goto Lnocpctor;
+            }
+        }
+        else
+        {
+        Lnocpctor:
+            buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies",
+                       argStruct.toChars(), arg.type.toChars(), tprm.toChars());
+        }
+
+        *pMessage = buf.extractChars();
+    }
+    return false;
+}
+
+/**
+ * Match a single parameter to an argument.
+ *
+ * This function is called by `TypeFunction.callMatch` while iterating over
+ * the list of parameter. Here we check if `arg` is a match for `p`,
+ * which is mostly about checking if `arg.type` converts to `p`'s type
+ * and some check about value reference.
+ *
+ * Params:
+ *   tf = The `TypeFunction`, only used for error reporting
+ *   p = The parameter of `tf` being matched
+ *   arg = Argument being passed (bound) to `p`
+ *   wildmatch = Wild (`inout`) matching level, derived from the full argument list
+ *   flag = A non-zero value means we're doing a partial ordering check
+ *          (no value semantic check)
+ *   sc = Scope we are in
+ *   pMessage = A buffer to write the error in, or `null`
+ *
+ * Returns: Whether `trailingArgs` match `p`.
+ */
+private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
+    Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage)
+{
+    //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars());
+    MATCH m;
+    Type targ = arg.type;
+    Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type;
+
+    if (p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid)
+        m = MATCH.convert;
+    else if (flag)
+    {
+        // for partial ordering, value is an irrelevant mockup, just look at the type
+        m = targ.implicitConvTo(tprm);
+    }
+    else
+    {
+        const isRef = p.isReference();
+        StructDeclaration argStruct, prmStruct;
+
+        // first look for a copy constructor
+        if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct)
+        {
+            // if the argument and the parameter are of the same unqualified struct type
+            argStruct = (cast(TypeStruct)targ).sym;
+            prmStruct = (cast(TypeStruct)tprm).sym;
+        }
+
+        // check if the copy constructor may be called to copy the argument
+        if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
+        {
+            if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
+                return MATCH.nomatch;
+            m = MATCH.exact;
+        }
+        else
+        {
+            import dmd.dcast : cimplicitConvTo;
+            m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm);
+        }
+    }
+
+    // Non-lvalues do not match ref or out parameters
+    if (p.isReference())
+    {
+        // https://issues.dlang.org/show_bug.cgi?id=13783
+        // Don't use toBasetype() to handle enum types.
+        Type ta = targ;
+        Type tp = tprm;
+        //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars());
+
+        if (m && !arg.isLvalue())
+        {
+            if (p.storageClass & STC.out_)
+            {
+                if (pMessage) *pMessage = tf.getParamError(arg, p);
+                return MATCH.nomatch;
+            }
+
+            if (arg.op == EXP.string_ && tp.ty == Tsarray)
+            {
+                if (ta.ty != Tsarray)
+                {
+                    Type tn = tp.nextOf().castMod(ta.nextOf().mod);
+                    dinteger_t dim = (cast(StringExp)arg).len;
+                    ta = tn.sarrayOf(dim);
+                }
+            }
+            else if (arg.op == EXP.slice && tp.ty == Tsarray)
+            {
+                // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
+                if (ta.ty != Tsarray)
+                {
+                    Type tn = ta.nextOf();
+                    dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
+                    ta = tn.sarrayOf(dim);
+                }
+            }
+            else if ((p.storageClass & STC.in_) && global.params.previewIn)
+            {
+                // Allow converting a literal to an `in` which is `ref`
+                if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray)
+                {
+                    Type tn = tp.nextOf();
+                    dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
+                    ta = tn.sarrayOf(dim);
+                }
+
+                // Need to make this a rvalue through a temporary
+                m = MATCH.convert;
+            }
+            else if (global.params.rvalueRefParam != FeatureState.enabled ||
+                     p.storageClass & STC.out_ ||
+                     !arg.type.isCopyable())  // can't copy to temp for ref parameter
+            {
+                if (pMessage) *pMessage = tf.getParamError(arg, p);
+                return MATCH.nomatch;
+            }
+            else
+            {
+                /* in functionParameters() we'll convert this
+                 * rvalue into a temporary
+                 */
+                m = MATCH.convert;
+            }
+        }
+
+        /* If the match is not already perfect or if the arg
+           is not a lvalue then try the `alias this` chain
+           see  https://issues.dlang.org/show_bug.cgi?id=15674
+           and https://issues.dlang.org/show_bug.cgi?id=21905
+        */
+        if (ta != tp || !arg.isLvalue())
+        {
+            Type firsttab = ta.toBasetype();
+            while (1)
+            {
+                Type tab = ta.toBasetype();
+                Type tat = tab.aliasthisOf();
+                if (!tat || !tat.implicitConvTo(tprm))
+                    break;
+                if (tat == tab || tat == firsttab)
+                    break;
+                ta = tat;
+            }
+        }
+
+        /* A ref variable should work like a head-const reference.
+         * e.g. disallows:
+         *  ref T      <- an lvalue of const(T) argument
+         *  ref T[dim] <- an lvalue of const(T[dim]) argument
+         */
+        if (!ta.constConv(tp))
+        {
+            if (pMessage) *pMessage = tf.getParamError(arg, p);
+            return MATCH.nomatch;
+        }
+    }
+    return m;
+}
+
+/**
+ * Match the remaining arguments `trailingArgs` with parameter `p`.
+ *
+ * Assume we already checked that `p` is the last parameter of `tf`,
+ * and we want to know whether the arguments would match `p`.
+ *
+ * Params:
+ *   tf = The `TypeFunction`, only used for error reporting
+ *   p = The last parameter of `tf` which is variadic
+ *   trailingArgs = The remaining arguments that should match `p`
+ *   pMessage = A buffer to write the error in, or `null`
+ *
+ * Returns: Whether `trailingArgs` match `p`.
+ */
+private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p,
+    Expression[] trailingArgs, const(char)** pMessage)
+{
+    Type tb = p.type.toBasetype();
+
+    switch (tb.ty)
+    {
+    case Tsarray:
+        TypeSArray tsa = cast(TypeSArray)tb;
+        dinteger_t sz = tsa.dim.toInteger();
+        if (sz != trailingArgs.length)
+        {
+            if (pMessage)
+                *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu",
+                    sz, trailingArgs.length);
+            return MATCH.nomatch;
+        }
+        goto case Tarray;
+    case Tarray:
+    {
+        MATCH match = MATCH.exact;
+        TypeArray ta = cast(TypeArray)tb;
+        foreach (arg; trailingArgs)
+        {
+            MATCH m;
+            assert(arg);
+
+            /* If lazy array of delegates,
+             * convert arg(s) to delegate(s)
+             */
+            Type tret = p.isLazyArray();
+            if (tret)
+            {
+                if (ta.next.equals(arg.type))
+                    m = MATCH.exact;
+                else if (tret.toBasetype().ty == Tvoid)
+                    m = MATCH.convert;
+                else
+                {
+                    m = arg.implicitConvTo(tret);
+                    if (m == MATCH.nomatch)
+                        m = arg.implicitConvTo(ta.next);
+                }
+            }
+            else
+                m = arg.implicitConvTo(ta.next);
+
+            if (m == MATCH.nomatch)
+            {
+                if (pMessage) *pMessage = tf.getParamError(arg, p);
+                return MATCH.nomatch;
+            }
+            if (m < match)
+                match = m;
+        }
+        return match;
+    }
+    case Tclass:
+        // We leave it up to the actual constructor call to do the matching.
+        return MATCH.exact;
+
+    default:
+        // We can have things as `foo(int[int] wat...)` but they only match
+        // with an associative array proper.
+        if (pMessage && trailingArgs.length) *pMessage = tf.getParamError(trailingArgs[0], p);
+        return MATCH.nomatch;
+    }
+}
index 3e614d88180ba22deb655293c8f3b054a34f4f6c..2b9c94cf7f2f0ed63b5d0f98388faed2605a4ce2 100644 (file)
@@ -221,7 +221,7 @@ public:
     virtual const char *kind();
     Type *copy() const;
     virtual Type *syntaxCopy();
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     bool equivalent(Type *t);
     // kludge for template.isType()
     DYNCAST dyncast() const override final { return DYNCAST_TYPE; }
@@ -877,7 +877,7 @@ public:
     static TypeTuple *create(Type *t1, Type *t2);
     const char *kind() override;
     TypeTuple *syntaxCopy() override;
-    bool equals(const RootObject *o) const override;
+    bool equals(const RootObject * const o) const override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
index 4f6903cbab721284a49b34cb0fa781a223477743..ca99b8bdc36e617910a39d82ac271da08806f85c 100644 (file)
@@ -1247,13 +1247,10 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 args2[0] = e.e2;
                 expandTuples(&args2);
                 MatchAccumulator m;
-                if (s)
+                functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
+                if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
                 {
-                    functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
-                    if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
-                    {
-                        return ErrorExp.get();
-                    }
+                    return ErrorExp.get();
                 }
                 if (m.count > 1)
                 {
index ce2769d38490441d04249beabd936d8ed486e0bc..ed85a5de96aad0a07f995a01a90ba0b9fb9ad87e 100644 (file)
@@ -2756,7 +2756,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
     {
         auto parameters = new AST.Parameters();
         VarArg varargs = VarArg.none;
-        int hasdefault = 0;
         StorageClass varargsStc;
 
         // Attributes allowed for ...
@@ -2921,27 +2920,23 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                         //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
                             //error("scope cannot be ref or out");
 
-                        if (tpl && token.value == TOK.identifier)
+                        const tv = peekNext();
+                        if (tpl && token.value == TOK.identifier &&
+                            (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot))
                         {
-                            const tv = peekNext();
-                            if (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot)
-                            {
-                                Identifier id = Identifier.generateId("__T");
-                                const loc = token.loc;
-                                at = new AST.TypeIdentifier(loc, id);
-                                if (!*tpl)
-                                    *tpl = new AST.TemplateParameters();
-                                AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
-                                (*tpl).push(tp);
-
-                                ai = token.ident;
-                                nextToken();
-                            }
-                            else goto _else;
+                            Identifier id = Identifier.generateId("__T");
+                            const loc = token.loc;
+                            at = new AST.TypeIdentifier(loc, id);
+                            if (!*tpl)
+                                *tpl = new AST.TemplateParameters();
+                            AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
+                            (*tpl).push(tp);
+
+                            ai = token.ident;
+                            nextToken();
                         }
                         else
                         {
-                        _else:
                             at = parseType(&ai);
                         }
                         ae = null;
@@ -2949,12 +2944,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                         {
                             nextToken();
                             ae = parseDefaultInitExp();
-                            hasdefault = 1;
-                        }
-                        else
-                        {
-                            if (hasdefault)
-                                error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars());
                         }
                         auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null);
                         if (udas)
@@ -4484,7 +4473,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
 
             const loc = token.loc;
             Identifier ident;
-
             auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
             assert(t);
             if (!tfirst)
@@ -4868,6 +4856,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     token.value == TOK.identifier && peekNext() == TOK.goesTo ||
                     token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
                         skipAttributes(peekPastParen(peek(&token)), &tk) &&
+                        (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
+                    token.value == TOK.auto_ && peekNext() == TOK.ref_ &&
+                        peekNext2() == TOK.leftParenthesis &&
+                        skipAttributes(peekPastParen(peek(peek(&token))), &tk) &&
                         (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
                    )
                 {
@@ -4879,6 +4871,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     // identifier => expression
                     // ref (parameters) { statements... }
                     // ref (parameters) => expression
+                    // auto ref (parameters) { statements... }
+                    // auto ref (parameters) => expression
 
                     s = parseFunctionLiteral();
 
@@ -5006,7 +5000,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         case TOK.delegate_:
             save = token.value;
             nextToken();
-            if (token.value == TOK.ref_)
+            if (token.value == TOK.auto_)
+            {
+                nextToken();
+                if (token.value == TOK.ref_)
+                {
+                    // function auto ref (parameters) { statements... }
+                    // delegate auto ref (parameters) { statements... }
+                    stc = STC.auto_ | STC.ref_;
+                    nextToken();
+                }
+                else
+                    error("`auto` can only be used as part of `auto ref` for function literal return values");
+            }
+            else if (token.value == TOK.ref_)
             {
                 // function ref (parameters) { statements... }
                 // delegate ref (parameters) { statements... }
@@ -5034,6 +5041,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             }
             goto case TOK.leftParenthesis;
 
+        case TOK.auto_:
+            {
+                nextToken();
+                if (token.value == TOK.ref_)
+                {
+                    // auto ref (parameters) => expression
+                    // auto ref (parameters) { statements... }
+                    stc = STC.auto_ | STC.ref_;
+                    nextToken();
+                }
+                else
+                    error("`auto` can only be used as part of `auto ref` for function literal return values");
+                goto case TOK.leftParenthesis;
+            }
         case TOK.ref_:
             {
                 // ref (parameters) => expression
@@ -5086,7 +5107,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
 
         auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
         tf = cast(AST.TypeFunction)tf.addSTC(stc);
-        auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null);
+        auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null, null, stc & STC.auto_);
 
         if (token.value == TOK.goesTo)
         {
@@ -5209,7 +5230,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             }
             else
             {
-                f.frequires.push(parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_));
+                auto ret = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
+                assert(ret);
+                f.frequires.push(ret);
                 requireDo = true;
             }
             goto L1;
@@ -6550,7 +6573,7 @@ LagainStc:
                 nextToken();
             if (token.value == TOK.semicolon)
                 nextToken();
-            s = null;
+            s = new AST.ErrorStatement;
             break;
         }
         if (pEndloc)
@@ -8394,6 +8417,22 @@ LagainStc:
             e = parseNewExp(null);
             break;
 
+        case TOK.auto_:
+            {
+                if (peekNext() == TOK.ref_ && peekNext2() == TOK.leftParenthesis)
+                {
+                    Token* tk = peekPastParen(peek(peek(&token)));
+                    if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
+                    {
+                        // auto ref (arguments) => expression
+                        // auto ref (arguments) { statements... }
+                        goto case_delegate;
+                    }
+                }
+                nextToken();
+                error("found `%s` when expecting `ref` and function literal following `auto`", token.toChars());
+                goto Lerr;
+            }
         case TOK.ref_:
             {
                 if (peekNext() == TOK.leftParenthesis)
@@ -8630,7 +8669,7 @@ LagainStc:
                     if (token.value != TOK.identifier)
                     {
                         error("identifier expected following `(type)`.");
-                        return null;
+                        return AST.ErrorExp.get();
                     }
                     e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
                     nextToken();
@@ -8749,7 +8788,8 @@ LagainStc:
                                     if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
                                     {
                                         error("identifier or new keyword expected following `(...)`.");
-                                        return null;
+                                        nextToken();
+                                        return AST.ErrorExp.get();
                                     }
                                     e = new AST.TypeExp(loc, t);
                                     e.parens = true;
index 0c92a9af54f18f9ec23e4126da457b3d9e40c342..b735dd98728ffcd5273755d8f0d3d924b9696294 100644 (file)
@@ -39,7 +39,7 @@ class RootObject
 public:
     RootObject() { }
 
-    virtual bool equals(const RootObject *o) const;
+    virtual bool equals(const RootObject * const o) const;
 
     /**
      * Pretty-print an Object. Useful for debugging the old-fashioned way.
index ad4487f55dcc2517e99ae6219537b631f601d3a5..d2f9c0ab04b0b7e45c908fe60c781cfd31c32c2d 100644 (file)
@@ -167,11 +167,18 @@ private extern(C++) final class Semantic3Visitor : Visitor
 
         sc = sc.push(tmix.argsym);
         sc = sc.push(tmix);
+
+        uint olderrors = global.errors;
+
         for (size_t i = 0; i < tmix.members.dim; i++)
         {
             Dsymbol s = (*tmix.members)[i];
             s.semantic3(sc);
         }
+
+        if (global.errors != olderrors)
+            errorSupplemental(tmix.loc, "parent scope from here: `mixin %s`", tmix.toChars());
+
         sc = sc.pop();
         sc.pop();
     }
@@ -969,6 +976,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
             /* Do the semantic analysis on the [in] preconditions and
              * [out] postconditions.
              */
+            immutable bool isnothrow = f.isnothrow && !(funcdecl.flags & FUNCFLAG.nothrowInprocess);
             if (freq)
             {
                 /* frequire is composed of the [in] contracts
@@ -980,10 +988,22 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require;
 
                 // BUG: need to error if accessing out parameters
-                // BUG: need to disallow returns and throws
+                // BUG: need to disallow returns
                 // BUG: verify that all in and ref parameters are read
                 freq = freq.statementSemantic(sc2);
-                freq.blockExit(funcdecl, false);
+
+                // @@@DEPRECATED_2.111@@@ - pass `isnothrow` instead of `false` to print a more detailed error msg`
+                const blockExit = freq.blockExit(funcdecl, false);
+                if (blockExit & BE.throw_)
+                {
+                    if (isnothrow)
+                        // @@@DEPRECATED_2.111@@@
+                        // Deprecated in 2.101, can be made an error in 2.111
+                        deprecation(funcdecl.loc, "`%s`: `in` contract may throw but function is marked as `nothrow`",
+                            funcdecl.toPrettyChars());
+                    else if (funcdecl.flags & FUNCFLAG.nothrowInprocess)
+                        f.isnothrow = false;
+                }
 
                 funcdecl.flags &= ~FUNCFLAG.noEH;
 
@@ -992,6 +1012,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 if (global.params.useIn == CHECKENABLE.off)
                     freq = null;
             }
+
             if (fens)
             {
                 /* fensure is composed of the [out] contracts
@@ -1017,7 +1038,19 @@ private extern(C++) final class Semantic3Visitor : Visitor
                     funcdecl.buildResultVar(scout, f.next);
 
                 fens = fens.statementSemantic(sc2);
-                fens.blockExit(funcdecl, false);
+
+                // @@@DEPRECATED_2.111@@@ - pass `isnothrow` instead of `false` to print a more detailed error msg`
+                const blockExit = fens.blockExit(funcdecl, false);
+                if (blockExit & BE.throw_)
+                {
+                    if (isnothrow)
+                        // @@@DEPRECATED_2.111@@@
+                        // Deprecated in 2.101, can be made an error in 2.111
+                        deprecation(funcdecl.loc, "`%s`: `out` contract may throw but function is marked as `nothrow`",
+                            funcdecl.toPrettyChars());
+                    else if (funcdecl.flags & FUNCFLAG.nothrowInprocess)
+                        f.isnothrow = false;
+                }
 
                 funcdecl.flags &= ~FUNCFLAG.noEH;
 
@@ -1144,7 +1177,6 @@ private extern(C++) final class Semantic3Visitor : Visitor
 
                             s = s.statementSemantic(sc2);
 
-                            immutable bool isnothrow = f.isnothrow && !(funcdecl.flags & FUNCFLAG.nothrowInprocess);
                             const blockexit = s.blockExit(funcdecl, isnothrow);
                             if (blockexit & BE.throw_)
                             {
index 5791a885068c8ea0c0db807da6e14f2c016610d3..0d7240f4ef44a11c3388eeb4ff767ae17bffcb38 100644 (file)
@@ -28,6 +28,7 @@ extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST
  */
 package mixin template ParseVisitMethods(AST)
 {
+    import dmd.root.array;
 
 //   Statement Nodes
 //===========================================================
@@ -46,7 +47,7 @@ package mixin template ParseVisitMethods(AST)
     override void visit(AST.CompileStatement s)
     {
         //printf("Visiting CompileStatement\n");
-        visitArgs(s.exps);
+        visitArgs(s.exps.peekSlice());
     }
 
     override void visit(AST.CompoundStatement s)
@@ -181,11 +182,9 @@ package mixin template ParseVisitMethods(AST)
             s.elsebody.accept(this);
     }
 
-    void visitArgs(AST.Expressions* expressions, AST.Expression basis = null)
+    private extern(D) void visitArgs(AST.Expression[] expressions, AST.Expression basis = null)
     {
-        if (!expressions || !expressions.dim)
-            return;
-        foreach (el; *expressions)
+        foreach (el; expressions)
         {
             if (!el)
                 el = basis;
@@ -197,8 +196,7 @@ package mixin template ParseVisitMethods(AST)
     override void visit(AST.PragmaStatement s)
     {
         //printf("Visiting PragmaStatement\n");
-        if (s.args && s.args.dim)
-            visitArgs(s.args);
+        visitArgs(s.args.peekSlice());
         if (s._body)
             s._body.accept(this);
     }
@@ -346,19 +344,14 @@ package mixin template ParseVisitMethods(AST)
             foreach (p; *td.origParameters)
                 p.accept(this);
         }
-        visitParameters(t.parameterList.parameters);
+        visitParameters(t.parameterList.parameters.peekSlice());
     }
 
-    void visitParameters(AST.Parameters* parameters)
+    private extern(D) final void visitParameters(AST.Parameter[] parameters)
     {
-        if (parameters)
+        foreach (i; 0 .. parameters.length)
         {
-            size_t dim = AST.Parameter.dim(parameters);
-            foreach(i; 0..dim)
-            {
-                AST.Parameter fparam = AST.Parameter.getNth(parameters, i);
-                fparam.accept(this);
-            }
+            parameters[i].accept(this);
         }
     }
 
@@ -469,7 +462,7 @@ package mixin template ParseVisitMethods(AST)
     override void visit(AST.TypeTuple t)
     {
         //printf("Visiting TypeTuple\n");
-        visitParameters(t.arguments);
+        visitParameters(t.arguments.peekSlice());
     }
 
     override void visit(AST.TypeSlice t)
@@ -487,7 +480,7 @@ package mixin template ParseVisitMethods(AST)
 
     override void visit(AST.TypeMixin t)
     {
-        visitArgs(t.exps);
+        visitArgs(t.exps.peekSlice());
     }
 
 //      Miscellaneous
@@ -571,8 +564,7 @@ package mixin template ParseVisitMethods(AST)
     override void visit(AST.PragmaDeclaration d)
     {
         //printf("Visiting PragmaDeclaration\n");
-        if (d.args && d.args.dim)
-            visitArgs(d.args);
+        visitArgs(d.args.peekSlice());
         visitAttribDeclaration(cast(AST.AttribDeclaration)d);
     }
 
@@ -580,24 +572,22 @@ package mixin template ParseVisitMethods(AST)
     {
         //printf("Visiting ConditionalDeclaration\n");
         d.condition.accept(this);
-        if (d.decl)
-            foreach (de; *d.decl)
-                de.accept(this);
-        if (d.elsedecl)
-            foreach (de; *d.elsedecl)
-                de.accept(this);
+        foreach (de; d.decl.peekSlice())
+            de.accept(this);
+        foreach (de; d.elsedecl.peekSlice())
+            de.accept(this);
     }
 
     override void visit(AST.CompileDeclaration d)
     {
         //printf("Visiting compileDeclaration\n");
-        visitArgs(d.exps);
+        visitArgs(d.exps.peekSlice());
     }
 
     override void visit(AST.UserAttributeDeclaration d)
     {
         //printf("Visiting UserAttributeDeclaration\n");
-        visitArgs(d.atts);
+        visitArgs(d.atts.peekSlice());
         visitAttribDeclaration(cast(AST.AttribDeclaration)d);
     }
 
@@ -791,6 +781,15 @@ package mixin template ParseVisitMethods(AST)
             s.accept(this);
     }
 
+    override void visit(AST.UnionDeclaration d)
+    {
+        //printf("Visiting UnionDeclaration\n");
+        if (!d.members)
+            return;
+        foreach (s; *d.members)
+            s.accept(this);
+    }
+
     override void visit(AST.ClassDeclaration d)
     {
         //printf("Visiting ClassDeclaration\n");
@@ -840,7 +839,7 @@ package mixin template ParseVisitMethods(AST)
         auto tf = f.type.isTypeFunction();
         if (!f.inferRetType && tf.next)
             visitType(tf.next);
-        visitParameters(tf.parameterList.parameters);
+        visitParameters(tf.parameterList.parameters.peekSlice());
         AST.CompoundStatement cs = f.fbody.isCompoundStatement();
         AST.Statement s = !cs ? f.fbody : null;
         AST.ReturnStatement rs = s ? s.isReturnStatement() : null;
@@ -946,7 +945,7 @@ package mixin template ParseVisitMethods(AST)
     override void visit(AST.ArrayLiteralExp e)
     {
         //printf("Visiting ArrayLiteralExp\n");
-        visitArgs(e.elements, e.basis);
+        visitArgs(e.elements.peekSlice(), e.basis);
     }
 
     override void visit(AST.AssocArrayLiteralExp e)
@@ -978,8 +977,7 @@ package mixin template ParseVisitMethods(AST)
         if (e.thisexp)
             e.thisexp.accept(this);
         visitType(e.newtype);
-        if (e.arguments && e.arguments.dim)
-            visitArgs(e.arguments);
+        visitArgs(e.arguments.peekSlice());
     }
 
     override void visit(AST.NewAnonClassExp e)
@@ -987,8 +985,7 @@ package mixin template ParseVisitMethods(AST)
         //printf("Visiting NewAnonClassExp\n");
         if (e.thisexp)
             e.thisexp.accept(this);
-        if (e.arguments && e.arguments.dim)
-            visitArgs(e.arguments);
+        visitArgs(e.arguments.peekSlice());
         if (e.cd)
             e.cd.accept(this);
     }
@@ -998,7 +995,7 @@ package mixin template ParseVisitMethods(AST)
         //printf("Visiting TupleExp\n");
         if (e.e0)
             e.e0.accept(this);
-        visitArgs(e.exps);
+        visitArgs(e.exps.peekSlice());
     }
 
     override void visit(AST.FuncExp e)
@@ -1056,7 +1053,7 @@ package mixin template ParseVisitMethods(AST)
     override void visit(AST.MixinExp e)
     {
         //printf("Visiting MixinExp\n");
-        visitArgs(e.exps);
+        visitArgs(e.exps.peekSlice());
     }
 
     override void visit(AST.ImportExp e)
@@ -1090,7 +1087,7 @@ package mixin template ParseVisitMethods(AST)
     {
         //printf("Visiting CallExp\n");
         e.e1.accept(this);
-        visitArgs(e.arguments);
+        visitArgs(e.arguments.peekSlice());
     }
 
     override void visit(AST.PtrExp e)
@@ -1124,7 +1121,7 @@ package mixin template ParseVisitMethods(AST)
     {
         //printf("Visiting ArrayExp\n");
         e.e1.accept(this);
-        visitArgs(e.arguments);
+        visitArgs(e.arguments.peekSlice());
     }
 
     override void visit(AST.PostExp e)
index b21ff7904ac18ec4364b38e022917e208696b311..0ef77052ec8e8a6231a50069736682801b4c4af3 100644 (file)
@@ -1388,6 +1388,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             // extended index), as we need to run semantic when `oidx` changes.
             size_t tupleOrigIdx = size_t.max;
             size_t tupleExtIdx = size_t.max;
+            bool hasDefault;
             foreach (oidx, oparam, eidx, eparam; tf.parameterList)
             {
                 // oparam (original param) will always have the default arg
@@ -1396,6 +1397,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                 // position to get the offset in it later on.
                 if (oparam.defaultArg)
                 {
+                    hasDefault = true;
                     // Get the obvious case out of the way
                     if (oparam is eparam)
                         errors |= !defaultArgSemantic(eparam, argsc);
@@ -1422,6 +1424,11 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                             eparam.defaultArg = (*te.exps)[eidx - tupleExtIdx];
                     }
                 }
+                else if (hasDefault)
+                {
+                    .error(loc, "default argument expected for `%s`", oparam.toChars());
+                    errors = true;
+                }
 
                 // We need to know the default argument to resolve `auto ref`,
                 // hence why this has to take place as the very last step.
@@ -2089,10 +2096,12 @@ extern (C++) Type merge(Type type)
  *  loc = the location where the property is encountered
  *  ident = the identifier of the property
  *  flag = if flag & 1, don't report "not a property" error and just return NULL.
+ *  src = expression for type `t` or null.
  * Returns:
  *      expression representing the property, or null if not a property and (flag & 1)
  */
-Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag)
+Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag,
+    Expression src = null)
 {
     Expression visitType(Type mt)
     {
@@ -2169,7 +2178,10 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
                         error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr);
                 else
                 {
-                    error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
+                    if (src)
+                        error(loc, "no property `%s` for `%s` of type `%s`", ident.toChars(), src.toChars(), mt.toPrettyChars(true));
+                    else
+                        error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
                     if (auto dsym = mt.toDsymbol(scope_))
                         if (auto sym = dsym.isAggregateDeclaration())
                         {
@@ -4457,7 +4469,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
 
 
 /************************
- * Get the the default initialization expression for a type.
+ * Get the default initialization expression for a type.
  * Params:
  *  mt = the type for which the init expression is returned
  *  loc = the location where the expression needs to be evaluated
index b0ce87019f09d090e1982e6e28041c9bdb7c84b8..fa5ec90a2bccb194035c509f9ddb302ec417dc07 100644 (file)
@@ -908,21 +908,12 @@ public:
 
            if ((postblit || destructor) && e->op != EXP::blit)
              {
-               /* Need to call postblit/destructor as part of assignment.
-                  Construction has already been handled by the front-end.  */
-               gcc_assert (e->op != EXP::construct);
-
-               /* So we can call postblits on const/immutable objects.  */
-               Type *tm = etype->unSharedOf ()->mutableOf ();
-               tree ti = build_typeinfo (e, tm);
-
-               /* Generate: _d_arraysetassign (t1.ptr, &t2, t1.length, ti);  */
-               result = build_libcall (LIBCALL_ARRAYSETASSIGN, Type::tvoid, 4,
-                                       d_array_ptr (t1),
-                                       build_address (t2),
-                                       d_array_length (t1), ti);
+               /* This case should have been rewritten to `_d_arraysetassign`
+                  in the semantic phase.  */
+               gcc_unreachable ();
              }
-           else if (integer_zerop (t2))
+
+           if (integer_zerop (t2))
              {
                tree size = size_mult_expr (d_array_length (t1),
                                            size_int (etype->size ()));
@@ -2473,6 +2464,20 @@ public:
        if (e->argprefix)
          result = compound_expr (build_expr (e->argprefix), result);
       }
+    else if (tb->ty == TY::Taarray)
+      {
+       /* Allocating memory for a new associative array.  */
+       tree arg = build_typeinfo (e, e->newtype);
+       tree mem = build_libcall (LIBCALL_AANEW, Type::tvoidptr, 1, arg);
+
+       /* Return an associative array pointed to by MEM.  */
+       tree aatype = build_ctype (tb);
+       vec <constructor_elt, va_gc> *ce = NULL;
+       CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem);
+
+       result = build_nop (build_ctype (e->type),
+                           build_constructor (aatype, ce));
+      }
     else
       gcc_unreachable ();
 
index 282f22c50730b39200c077c7fc435d83b0d504f5..f576bef4e59d99ba232fab446c91b12862dfd66f 100644 (file)
@@ -115,10 +115,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 array assignments from a single element.  */
-DEF_D_RUNTIME (ARRAYSETASSIGN, "_d_arraysetassign", RT(VOIDPTR),
-              P4(VOIDPTR, VOIDPTR, SIZE_T, CONST_TYPEINFO), 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),
@@ -140,6 +136,7 @@ DEF_D_RUNTIME (ARRAYAPPENDWD, "_d_arrayappendwd", RT(ARRAY_VOID),
 /* Used for allocating a new associative array.  */
 DEF_D_RUNTIME (ASSOCARRAYLITERALTX, "_d_assocarrayliteralTX", RT(VOIDPTR),
               P3(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID), 0)
+DEF_D_RUNTIME (AANEW, "_aaNew", RT(VOIDPTR), P1(CONST_TYPEINFO), 0)
 
 /* Used for value equality of two associative arrays.  */
 DEF_D_RUNTIME (AAEQUAL, "_aaEqual", RT(INT),
index 076e29baece1e2f4d6e3a066cb3c67440f60b5c2..abe956c80948bcea2b18759d9dee507a9acfda1d 100644 (file)
@@ -151,19 +151,19 @@ static assert(Error!( uint*, int* ));
 static assert(is( X!( int function(), int function() ) == int function() ));
 
 // void pointer
-static assert(is( X!( void*, int* ) == int* ));
-static assert(is( X!( int*, void* ) == int* ));
-static assert(is( X!( const(int)*, void* ) == const(int)* ));
-static assert(is( X!( const(int*), void* ) == const(int*) ));
-static assert(is( X!( int*, const(void)* ) == int* )); // `const`
-static assert(is( X!( int*, const(void*) ) == int* )); // `const`
-static assert(is( X!( int*, shared(void*) ) == int* )); // should fail
-static assert(is( X!( int*, shared(void)* ) == int* )); // should fail
+static assert(is( X!( void*, int* ) == void* ));
+static assert(is( X!( int*, void* ) == void* ));
+static assert(is( X!( const(int)*, void* ) == void* ));
+static assert(is( X!( const(int*), void* ) == void* ));
+static assert(is( X!( int*, const(void)* ) == const(void)* )); // `const`
+static assert(is( X!( int*, const(void*) ) == const(void*) )); // `const`
+static assert(is( X!( int*, shared(void*) ) == shared(void*) )); // should fail
+static assert(is( X!( int*, shared(void)* ) == shared(void)* )); // should fail
 
 static assert(Error!( int**, void** )); // should work
 
-static assert(is( X!( void*, int function() ) == int function() ));
-static assert(is( X!( immutable(void*), int function() ) == int function() )); // `const`
+static assert(is( X!( void*, int function() ) == void* ));
+static assert(is( X!( immutable(void*), int function() ) == immutable(void*) )); // `const`
 
 // implicit conversion
 static assert(is( X!( int*, const(int*) ) == const(int*) ));
diff --git a/gcc/testsuite/gdc.test/compilable/imports/cimports2a.i b/gcc/testsuite/gdc.test/compilable/imports/cimports2a.i
new file mode 100644 (file)
index 0000000..c8ff976
--- /dev/null
@@ -0,0 +1,4 @@
+extern int xx;
+
+typedef struct Foo *FooRef;
+FooRef make_foo(void);
diff --git a/gcc/testsuite/gdc.test/compilable/imports/cimports2b.i b/gcc/testsuite/gdc.test/compilable/imports/cimports2b.i
new file mode 100644 (file)
index 0000000..03b22b2
--- /dev/null
@@ -0,0 +1,4 @@
+extern int xx;
+
+typedef struct Foo *FooRef;
+void free_foo(FooRef foo);
diff --git a/gcc/testsuite/gdc.test/compilable/imports/format23327.d b/gcc/testsuite/gdc.test/compilable/imports/format23327.d
new file mode 100644 (file)
index 0000000..de9b957
--- /dev/null
@@ -0,0 +1,7 @@
+module imports.format23327;
+
+import imports.format23327.write;
+
+immutable(string) format23327() { }
+
+import imports.format23327.internal.write;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/format23327/write.d b/gcc/testsuite/gdc.test/compilable/imports/format23327/write.d
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gcc/testsuite/gdc.test/compilable/segfaultgolf.d b/gcc/testsuite/gdc.test/compilable/segfaultgolf.d
new file mode 100644 (file)
index 0000000..2ea125f
--- /dev/null
@@ -0,0 +1,50 @@
+// https://issues.dlang.org/show_bug.cgi?id=23351
+enum strings =
+[
+"a[(b).",
+"[(a)(b).",
+"a(={@.()(",
+"a[b,[(c).",
+"a[b#([(c).",
+"[a@b[(c).",
+"[((a).",
+"[a)b[(c).",
+"a[b)[(c).",
+"a(b[(c).",
+"a[b()c[(d).",
+"a[(b[(c).",
+"a(b[(c).",
+"[(@@a b[(c).",
+"a[(!b)c[(d).",
+"[(^a)b[(c).",
+"a(b[(c).",
+"~[a.b[(c).",
+"[a).[(b c d(e[(f).",
+"[((a).",
+"[a}b[(c).",
+"a[b[c..(d).",
+"[1a.[(b).",
+"a[({in){,",
+"a[^in(b[c=])S....,",
+"a[({in[({)){,"
+];
+template KidNamedFinger(T)
+{
+
+}
+void dummy()
+{
+    static foreach(str; strings)
+    {
+        /*
+            The above strings are all gibberish, they should
+            fail to parse but not segfault the compiler.
+        */
+        {
+            enum exp = __traits(compiles, mixin(str));
+            static assert(!exp);
+            enum t = __traits(compiles, KidNamedFinger!(mixin(str)));
+            static assert(!t);
+        }
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/statictemplatethis.d b/gcc/testsuite/gdc.test/compilable/statictemplatethis.d
new file mode 100644 (file)
index 0000000..0236f2d
--- /dev/null
@@ -0,0 +1,45 @@
+mixin template Constructors(){
+    this(){ }
+    this()immutable{ }
+    this()shared{ }
+}
+
+class A {
+public:
+    static T getInstance(this T)() {
+        return new T();
+    }
+private:
+    mixin Constructors;
+}
+
+class B : A {
+private:
+    mixin Constructors;
+}
+
+void f(){
+    auto a = (new A).getInstance;
+    auto b = (new B).getInstance;
+    static assert(is(typeof(a) == A));
+    static assert(is(typeof(b) == B));
+
+    auto ca = (new immutable A).getInstance;
+    auto sb = (new shared B).getInstance;
+    static assert(is(typeof(ca) == immutable A));
+    static assert(is(typeof(sb) == shared B));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=10488
+version(none)
+void g(){
+    auto a = A.getInstance();
+    auto b = B.getInstance();
+    static assert(is(typeof(a)==A));
+    static assert(is(typeof(b)==B));
+
+    auto ai = (immutable(A)).getInstance();
+    auto bs = (shared(B)).getInstance();
+    static assert(is(typeof(ai)==immutable(A)));
+    static assert(is(typeof(bs)==shared(B)));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test13123.d b/gcc/testsuite/gdc.test/compilable/test13123.d
new file mode 100644 (file)
index 0000000..881eb1b
--- /dev/null
@@ -0,0 +1,38 @@
+auto inferNothrow()
+in
+{
+}
+out
+{
+}
+do
+{
+    return 1;
+}
+
+auto dontInferNothrowIn()
+in
+{
+    throw new Exception(null);
+}
+do
+{
+    return 1;
+}
+
+auto dontInferNothrowOut()
+out
+{
+    throw new Exception(null);
+}
+do
+{
+    return 1;
+}
+
+enum isNothrow(Attr...) = (Attr.length >= 1)
+    && (Attr[0] == "nothrow" || isNothrow!(Attr[1 .. $]));
+
+static assert(isNothrow!(__traits(getFunctionAttributes, inferNothrow)));
+static assert(!isNothrow!(__traits(getFunctionAttributes, dontInferNothrowIn)));
+static assert(!isNothrow!(__traits(getFunctionAttributes, dontInferNothrowOut)));
diff --git a/gcc/testsuite/gdc.test/compilable/test21243.d b/gcc/testsuite/gdc.test/compilable/test21243.d
new file mode 100644 (file)
index 0000000..20838dc
--- /dev/null
@@ -0,0 +1,21 @@
+// Parsing - expressions
+auto a = auto ref (int x) => x;
+auto b = auto ref (int x) { return x; };
+auto c = function auto ref (int x) { return x; };
+auto d = delegate auto ref (int x) { return x; };
+
+// Parsing - aliases
+alias e = auto ref (int x) => x;
+alias f = auto ref (int x) { return x; };
+alias g = function auto ref (int x) { return x; };
+alias h = delegate auto ref (int x) { return x; };
+
+// Semantic
+void test()
+{
+    alias fun(alias x) = auto ref () => x;
+    int n = 123;
+    auto _ = fun!123();
+    static assert(!__traits(compiles, &fun!123())); // rvalue
+    fun!n() = 456; // lvalue
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21956.d b/gcc/testsuite/gdc.test/compilable/test21956.d
new file mode 100644 (file)
index 0000000..64ebc55
--- /dev/null
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=21956
+
+noreturn[noreturn] nrnr;
+
+void gun()
+{
+    foreach (a; nrnr){}
+}
+
+int main()
+{
+    noreturn[] empty;
+    int val;
+    foreach(el; empty) val++;
+    return val;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test22674.d b/gcc/testsuite/gdc.test/compilable/test22674.d
new file mode 100644 (file)
index 0000000..cc6e3bb
--- /dev/null
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=22674
+// EXTRA_FILES: imports/cimports2a.i imports/cimports2b.i
+
+import imports.cimports2a;
+import imports.cimports2b;
+
+void do_foo(){
+    FooRef f = make_foo(); // use_foo.d(5)
+    free_foo(f);           // use_foo.d(6)
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test23173.d b/gcc/testsuite/gdc.test/compilable/test23173.d
new file mode 100644 (file)
index 0000000..6b16132
--- /dev/null
@@ -0,0 +1,6 @@
+// REQUIRED_ARGS: -o-
+// https://issues.dlang.org/show_bug.cgi?id=23173
+
+mixin("long l = ", long.min, ";");
+static assert(mixin(long.min) == long.min);
+static assert(is(typeof(mixin(long.min)) == long));
diff --git a/gcc/testsuite/gdc.test/compilable/test23258.d b/gcc/testsuite/gdc.test/compilable/test23258.d
new file mode 100644 (file)
index 0000000..1e8e91b
--- /dev/null
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=23258
+
+struct SumType(Types...)
+{
+    this(Types[0])
+    {
+    }
+    this(Types[1])
+    {
+    }
+}
+
+alias A2 = SumType!(C1[], C2[]);
+
+class C1
+{
+}
+
+class C2
+{
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test23306.d b/gcc/testsuite/gdc.test/compilable/test23306.d
new file mode 100644 (file)
index 0000000..81b51f6
--- /dev/null
@@ -0,0 +1,7 @@
+class A {
+        @disable new();
+}
+
+void main() {
+        scope A a = new A();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test23327.d b/gcc/testsuite/gdc.test/compilable/test23327.d
new file mode 100644 (file)
index 0000000..bbb6346
--- /dev/null
@@ -0,0 +1,3 @@
+// https://issues.dlang.org/show_bug.cgi?id=23327
+// EXTRA_FILES: imports/format23327.d imports/format23327/write.d
+import imports.format23327;
diff --git a/gcc/testsuite/gdc.test/compilable/vararg.d b/gcc/testsuite/gdc.test/compilable/vararg.d
new file mode 100644 (file)
index 0000000..79826a0
--- /dev/null
@@ -0,0 +1,20 @@
+void main ()
+{
+    variance([1.0, 2, 3]);
+}
+
+alias meanType(T) = T;
+
+template variance(bool stable = true)
+{
+    void variance(Range)(Range r, bool isPopulation = false)
+    {
+        .variance!(double, stable)(r, isPopulation);
+    }
+}
+
+template variance(F, bool stable = true)
+{
+    void variance(Range)(Range r, bool isPopulation = false) {}
+    void variance(scope const F[] ar...) {}
+}
index 72becf2b53ac6ff156c6fa3dfbd2913f13817f34..84d0ad4696b64355a81b005185a04503f81a09ae 100644 (file)
@@ -2,7 +2,7 @@
 EXTRA_FILES: imports/a10169.d
 TEST_OUTPUT:
 ---
-fail_compilation/diag10169.d(12): Error: no property `x` for type `imports.a10169.B`
+fail_compilation/diag10169.d(12): Error: no property `x` for `B(0)` of type `imports.a10169.B`
 ---
 */
 import imports.a10169;
index f18341f0d03dcf6226f441850fc1ba5d762c0e79..80c7f5e56ea289357f2d5e5cbc9c5970d052e8c2 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag10783.d(14): Error: no property `type` for type `diag10783.Event`
+fail_compilation/diag10783.d(14): Error: no property `type` for `event` of type `diag10783.Event`
 fail_compilation/diag10783.d(14): Error: undefined identifier `En`
 ---
 */
index 5d908f7cbf3f582d533f7506929815685774ebb8..9b5761f7a575943e658408a915d1c27a74aaa62e 100644 (file)
@@ -1,10 +1,12 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag13528.d(13): Error: value of `this` is not known at compile time
-fail_compilation/diag13528.d(13):        while evaluating `pragma(msg, __traits(getMember, A, "foo"))`
+fail_compilation/diag13528.d(6): Error: value of `this` is not known at compile time
+fail_compilation/diag13528.d(6):        while evaluating `pragma(msg, __traits(getMember, A, "foo"))`
+fail_compilation/diag13528.d(12):        parent scope from here: `mixin MyTemplate!()`
 ---
 */
+#line 1
 
 mixin template MyTemplate()
 {
index 6447f5e59962c577815ce13bb171e7d8c21a8948..fa7c6114ad60a4f99daef91608217017f8ad5b3c 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag14145.d(15): Error: no property `i` for type `diag14145.main.Capture!(i)`
+fail_compilation/diag14145.d(15): Error: no property `i` for `_` of type `diag14145.main.Capture!(i)`
 fail_compilation/diag14145.d(15):        potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
 fail_compilation/diag14145.d(34): Error: expression `*this.ptr` of type `shared(int)` is not implicitly convertible to return type `ref int`
 fail_compilation/diag14145.d(16): Error: template instance `diag14145.main.Capture!(i).Capture.opDispatch!"i"` error instantiating
index 1c614083bfc211ccfa16728038f9de240c3a627c..e4cb2a7603a5687974d6abbff99ae69664101642 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag15713.d(19): Error: no property `widthSign` for type `diag15713.WrData.Data`
+fail_compilation/diag15713.d(19): Error: no property `widthSign` for `this` of type `diag15713.WrData.Data`
 fail_compilation/diag15713.d(39): Error: template instance `diag15713.conwritefImpl!("parse-int", "width", "\n", Data(null))` error instantiating
 fail_compilation/diag15713.d(44):        instantiated from here: `conwritefImpl!("main", "\n", Data(null))`
 fail_compilation/diag15713.d(49):        instantiated from here: `fdwritef!()`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag23355.d b/gcc/testsuite/gdc.test/fail_compilation/diag23355.d
new file mode 100644 (file)
index 0000000..586cbb0
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag23355.d(1): Error: undefined identifier `n`
+fail_compilation/diag23355.d(4): Error: none of the overloads of template `diag23355.ffi1` are callable using argument types `!()(int[4])`
+fail_compilation/diag23355.d(1):        Candidate is: `ffi1(T)(T[n] s)`
+fail_compilation/diag23355.d(2): Error: undefined identifier `n`
+fail_compilation/diag23355.d(4): Error: none of the overloads of template `diag23355.ffi2` are callable using argument types `!()(int[4])`
+fail_compilation/diag23355.d(2):        Candidate is: `ffi2()(T[n] s)`
+---
+*/
+#line 1
+void ffi1(T)(T[n] s) { }
+void ffi2()(T[n] s) { }
+
+void main() { int[4] x; ffi1(x); ffi2(x); }
index 445f6d5dddf5229209ef2d67d6e807363aa236e6..c4cbc721d860f7f199bf85fcc22660f8b7ee215f 100644 (file)
@@ -1,4 +1,3 @@
-// REQUIRED_ARGS: -de
 /*
 TEST_OUTPUT:
 ---
@@ -8,6 +7,7 @@ fail_compilation/diag3438.d(20): Error: constructor `diag3438.F5.this` is marked
 fail_compilation/diag3438.d(20):        Use `@disable this();` if you want to disable default initialization.
 fail_compilation/diag3438.d(21): Error: constructor `diag3438.F6.this` is marked `@disable`, so it cannot have default arguments for all parameters.
 fail_compilation/diag3438.d(21):        Use `@disable this();` if you want to disable default initialization.
+fail_compilation/diag3438.d(24): Error: default argument expected for `y`
 ---
 */
 
@@ -19,3 +19,6 @@ struct F3 { this(...) { } } // ok
 struct F4 { this(int[] x...) { } }  // ok
 struct F5 { @disable this(int x = 1); }
 struct F6 { @disable this(int x = 1) { } }
+
+// Make sure the deprecation doesn't interfere w/ the check for default arguments
+struct S { this(int x = 1, int y) { } }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3438b.d b/gcc/testsuite/gdc.test/fail_compilation/diag3438b.d
deleted file mode 100644 (file)
index 46a197d..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/diag3438b.d(9): Error: default argument expected for `y`
----
-*/
-
-// Make sure the deprecation doesn't interfere w/ the check for default arguments
-struct S { this(int x = 1, int y) { } }
index 9e0dadd148224ff2555c1dc9bd825c751ce0a96b..7cf3023aedbdf163f05f9760bf379074a131e137 100644 (file)
@@ -1,10 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag8894.d(16): Error: no property `x` for type `diag8894.Foo`
-fail_compilation/diag8894.d(17): Error: no property `y` for type `diag8894.Foo`
-fail_compilation/diag8894.d(18): Error: no property `x` for type `diag8894.Foo`
-fail_compilation/diag8894.d(19): Error: no property `x` for type `diag8894.Foo`
+fail_compilation/diag8894.d(16): Error: no property `x` for `f` of type `diag8894.Foo`
+fail_compilation/diag8894.d(17): Error: no property `y` for `f` of type `diag8894.Foo`
+fail_compilation/diag8894.d(18): Error: no property `x` for `f` of type `diag8894.Foo`
+fail_compilation/diag8894.d(19): Error: no property `x` for `f` of type `diag8894.Foo`
 ---
 */
 
index bf04a517c71a76bf9be018352c3b31dc9670997d..324d21733af0686acee0455ac92aae256261731d 100644 (file)
@@ -2,11 +2,11 @@
 EXTRA_FILES: imports/dip22a.d
 TEST_OUTPUT:
 ---
-fail_compilation/dip22a.d(16): Error: no property `bar` for type `imports.dip22a.Klass`
-fail_compilation/dip22a.d(17): Error: no property `bar` for type `imports.dip22a.Struct`
+fail_compilation/dip22a.d(16): Error: no property `bar` for `new Klass` of type `imports.dip22a.Klass`
+fail_compilation/dip22a.d(17): Error: no property `bar` for `Struct()` of type `imports.dip22a.Struct`
 fail_compilation/dip22a.d(18): Error: undefined identifier `bar` in module `imports.dip22a`
-fail_compilation/dip22a.d(19): Error: no property `bar` for type `void`
-fail_compilation/dip22a.d(20): Error: no property `bar` for type `int`
+fail_compilation/dip22a.d(19): Error: no property `bar` for `Template!int` of type `void`
+fail_compilation/dip22a.d(20): Error: no property `bar` for `12` of type `int`
 ---
 */
 import imports.dip22a;
index 33bee254589712867e2dc301eb51785f16709664..92e07342d49b008e23111763c4b7dbd2ab63201e 100644 (file)
@@ -1,14 +1,15 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/e15876_1.d(15): Error: valid scope identifiers are `exit`, `failure`, or `success`, not `x`
-fail_compilation/e15876_1.d(16): Error: found `End of File` when expecting `)`
-fail_compilation/e15876_1.d(16): Error: found `End of File` instead of statement
-fail_compilation/e15876_1.d(16): Error: found `End of File` when expecting `}` following compound statement
-fail_compilation/e15876_1.d(16): Error: found `End of File` when expecting `]`
-fail_compilation/e15876_1.d(16): Error: no identifier for declarator `o[()
+fail_compilation/e15876_1.d(16): Error: valid scope identifiers are `exit`, `failure`, or `success`, not `x`
+fail_compilation/e15876_1.d(17): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_1.d(17): Error: found `End of File` instead of statement
+fail_compilation/e15876_1.d(17): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_1.d(17): Error: found `End of File` when expecting `]`
+fail_compilation/e15876_1.d(17): Error: no identifier for declarator `o[()
 {
-scope(exit) }
+scope(exit) __error__
+}
 ]`
 ---
 */
index ae5f77a33e8e5c70d2d00c189d950cfd48bce4be..fe7d546f51b7b6a9ae2fbb7ee87bcadb50b4f011 100644 (file)
@@ -1,25 +1,27 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/e15876_3.d(25): Error: unexpected `(` in declarator
-fail_compilation/e15876_3.d(25): Error: basic type expected, not `=`
-fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `(`
-fail_compilation/e15876_3.d(26): Error: found `End of File` instead of statement
-fail_compilation/e15876_3.d(26): Error: expression expected, not `End of File`
-fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `;` following `for` condition
-fail_compilation/e15876_3.d(26): Error: expression expected, not `End of File`
-fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `)`
-fail_compilation/e15876_3.d(26): Error: found `End of File` instead of statement
-fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `}` following compound statement
-fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `)`
-fail_compilation/e15876_3.d(26): Error: no identifier for declarator `d(_error_ = ()
+fail_compilation/e15876_3.d(27): Error: unexpected `(` in declarator
+fail_compilation/e15876_3.d(27): Error: basic type expected, not `=`
+fail_compilation/e15876_3.d(28): Error: found `End of File` when expecting `(`
+fail_compilation/e15876_3.d(28): Error: found `End of File` instead of statement
+fail_compilation/e15876_3.d(28): Error: expression expected, not `End of File`
+fail_compilation/e15876_3.d(28): Error: found `End of File` when expecting `;` following `for` condition
+fail_compilation/e15876_3.d(28): Error: expression expected, not `End of File`
+fail_compilation/e15876_3.d(28): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_3.d(28): Error: found `End of File` instead of statement
+fail_compilation/e15876_3.d(28): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_3.d(28): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_3.d(28): Error: no identifier for declarator `d(_error_ = ()
 {
-for (; 0; 0)
+for (__error__
+ 0; 0)
 {
+__error__
 }
 }
 )`
-fail_compilation/e15876_3.d(26): Error: semicolon expected following function declaration
+fail_compilation/e15876_3.d(28): Error: semicolon expected following function declaration
 ---
 */
 d(={for
index 6f46633f002c70eee3f9cfaf4e0cb3baf356b88a..f4bd407b9c0eb84a61bff04dfd1c6b3779ca02a0 100644 (file)
@@ -1,20 +1,22 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/e15876_4.d(23): Error: found `)` when expecting `(`
-fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `(`
-fail_compilation/e15876_4.d(24): Error: found `End of File` instead of statement
-fail_compilation/e15876_4.d(24): Error: expression expected, not `End of File`
-fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `;` following `for` condition
-fail_compilation/e15876_4.d(24): Error: expression expected, not `End of File`
-fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `)`
-fail_compilation/e15876_4.d(24): Error: found `End of File` instead of statement
-fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `}` following compound statement
-fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `)`
-fail_compilation/e15876_4.d(24): Error: no identifier for declarator `typeof(()
+fail_compilation/e15876_4.d(25): Error: found `)` when expecting `(`
+fail_compilation/e15876_4.d(26): Error: found `End of File` when expecting `(`
+fail_compilation/e15876_4.d(26): Error: found `End of File` instead of statement
+fail_compilation/e15876_4.d(26): Error: expression expected, not `End of File`
+fail_compilation/e15876_4.d(26): Error: found `End of File` when expecting `;` following `for` condition
+fail_compilation/e15876_4.d(26): Error: expression expected, not `End of File`
+fail_compilation/e15876_4.d(26): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_4.d(26): Error: found `End of File` instead of statement
+fail_compilation/e15876_4.d(26): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_4.d(26): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_4.d(26): Error: no identifier for declarator `typeof(()
 {
-for (; 0; 0)
+for (__error__
+ 0; 0)
 {
+__error__
 }
 }
 )`
index e969b2493f73962970c088d5e9d2b76368d19bb0..cfda8f4d9ad00a3fd1b653bc2ba079c9575cb074 100644 (file)
@@ -1,27 +1,28 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail10968.d(42): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(42): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(30):        `fail10968.SA.__postblit` is declared here
 fail_compilation/fail10968.d(43): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(43): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(30):        `fail10968.SA.__postblit` is declared here
+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 `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(30):        `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_arrayassign_l!(SA[], SA)._d_arrayassign_l`
-fail_compilation/fail10968.d(47): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(47): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(30):        `fail10968.SA.__postblit` is declared here
+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`
+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`
 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(30):        `fail10968.SA.__postblit` is declared here
-fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor`
+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 `fail10968.SA.__postblit`
 fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
-fail_compilation/fail10968.d(30):        `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_arrayctor!(SA[], SA)._d_arrayctor`
+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`
+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`
 ---
 */
 
@@ -52,12 +53,12 @@ void bar() pure @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail10968.d(75): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
 fail_compilation/fail10968.d(76): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
 fail_compilation/fail10968.d(77): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
-fail_compilation/fail10968.d(80): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(78): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
 fail_compilation/fail10968.d(81): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
 fail_compilation/fail10968.d(82): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(83): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
 ---
 */
 
index 70e9d0cd4be125f1f4f84418a342cfea477d94c9..8d5af747ce15406fb80a89cb25f81569ce8ea9ba 100644 (file)
@@ -3,8 +3,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail121.d(23): Error: no property `typeinfo` for type `fail121.myobject`
-fail_compilation/fail121.d(23): Error: no property `typeinfo` for type `int`
+fail_compilation/fail121.d(23): Error: no property `typeinfo` for `list[1]` of type `fail121.myobject`
+fail_compilation/fail121.d(23): Error: no property `typeinfo` for `i` of type `int`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13123.d b/gcc/testsuite/gdc.test/fail_compilation/fail13123.d
new file mode 100644 (file)
index 0000000..7784cba
--- /dev/null
@@ -0,0 +1,21 @@
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail13123.d(10): Deprecation: `fail13123.test`: `in` contract may throw but function is marked as `nothrow`
+fail_compilation/fail13123.d(10): Deprecation: `fail13123.test`: `out` contract may throw but function is marked as `nothrow`
+---
+*/
+
+void test() nothrow
+in
+{
+    throw new Exception(null);
+}
+out
+{
+    throw new Exception(null);
+}
+do
+{
+}
index 3571e38b29ebdb91d1df6864abaafaceec9a8ff4..39e7cb9eb7377b43e6f301380ceb679b7db2b283 100644 (file)
@@ -4,9 +4,8 @@ EXTRA_FILES: imports/fail17646.d
 TEST_OUTPUT:
 ---
 fail_compilation/imports/fail17646.d(10): Error: found `}` instead of statement
-fail_compilation/imports/fail17646.d(7): Error: function `imports.fail17646.allTestData!"".allTestData` has no `return` statement, but is expected to return a value of type `const(TestData)[]`
-fail_compilation/fail17646.d(16): Error: template instance `imports.fail17646.allTestData!""` error instantiating
-fail_compilation/fail17646.d(19):        instantiated from here: `runTests!""`
+fail_compilation/fail17646.d(11): Error: function `fail17646.runTests!"".runTests` has no `return` statement, but is expected to return a value of type `int`
+fail_compilation/fail17646.d(18): Error: template instance `fail17646.runTests!""` error instantiating
 ---
 */
 int runTests(Modules...)()
index 531d1ed17ef70a969b7e75699a9c42a0d2f9a1e8..0fb56d315e97ae7f8da6f41e0ce0b76b2288834b 100644 (file)
@@ -1,8 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail18892.d(20): Error: no property `foo` for type `fail18892.MT`
-fail_compilation/fail18892.d(21): Error: no property `foo` for type `fail18892.MT`
+fail_compilation/fail18892.d(20): Error: no property `foo` for `a` of type `fail18892.MT`
+fail_compilation/fail18892.d(21): Error: no property `foo` for `MT` of type `fail18892.MT`
 ---
 */
 
index 09732171b86586eabbf639fdf3b12610cb4c8666..a8156fe7d1efc7347457f29df1c443ae25144d14 100644 (file)
@@ -1,9 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail18970.d(24): Error: no property `y` for type `fail18970.S`
+fail_compilation/fail18970.d(24): Error: no property `y` for `S()` of type `fail18970.S`
 fail_compilation/fail18970.d(24):        potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
-fail_compilation/fail18970.d(31): Error: no property `yyy` for type `fail18970.S2`
+fail_compilation/fail18970.d(31): Error: no property `yyy` for `this` of type `fail18970.S2`
 fail_compilation/fail18970.d(31):        potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
 ---
 */
index 975657052215e2000d5bd059917ca8f1b68026e1..04e36f623ff79be2ab4879c8d4f8790cdaf05575 100644 (file)
@@ -2,7 +2,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail18979.d(13): Error: no property `__ctor` for type `imports.imp18979.Foo`
+fail_compilation/fail18979.d(13): Error: no property `__ctor` for `Foo()` of type `imports.imp18979.Foo`
 ----
 */
 
index 6b740ac69456d53d9b5cda04cc71369b87ff7fa0..40fafcdc1ae0e823f4bb72b7ae93418c4a0a5320 100644 (file)
@@ -1,8 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail19103.d(12): Error: no property `puts` for type `fail19103.C`
-fail_compilation/fail19103.d(14): Error: no property `puts` for type `fail19103.S1`
+fail_compilation/fail19103.d(12): Error: no property `puts` for `new C` of type `fail19103.C`
+fail_compilation/fail19103.d(14): Error: no property `puts` for `s1` of type `fail19103.S1`
 fail_compilation/fail19103.d(16): Error: no property `puts` for type `S2`, did you mean `core.stdc.stdio.puts`?
 ---
 */
index 7d1c6e516e1b4cd21c4ab5bfee58da37ed5b1f32..0076091b8a1cacb07099fa538b23c0f833086f76 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail19687.d(17): Error: no property `nonexisting` for type `string`
+fail_compilation/fail19687.d(17): Error: no property `nonexisting` for `""` of type `string`
 ---
 */
 
index fe2655e1eda5d4d8b9ab44c811052374865f2a56..c880923ae407d3be405238e82e3dce2518a954c2 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail19913.d(11): Error: no property `b` for type `int`
+fail_compilation/fail19913.d(11): Error: no property `b` for `a` of type `int`
 fail_compilation/fail19913.d(11): Error: mixin `fail19913.S.b!()` is not defined
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21243.d b/gcc/testsuite/gdc.test/fail_compilation/fail21243.d
new file mode 100644 (file)
index 0000000..25df235
--- /dev/null
@@ -0,0 +1,19 @@
+/+ TEST_OUTPUT:
+---
+fail_compilation/fail21243.d(16): Error: found `(` when expecting `ref` and function literal following `auto`
+fail_compilation/fail21243.d(16): Error: semicolon expected following auto declaration, not `int`
+fail_compilation/fail21243.d(16): Error: semicolon needed to end declaration of `x` instead of `)`
+fail_compilation/fail21243.d(16): Error: declaration expected, not `)`
+fail_compilation/fail21243.d(17): Error: `auto` can only be used as part of `auto ref` for function literal return values
+fail_compilation/fail21243.d(18): Error: basic type expected, not `(`
+fail_compilation/fail21243.d(18): Error: function declaration without return type. (Note that constructors are always named `this`)
+fail_compilation/fail21243.d(18): Deprecation: storage class `auto` has no effect in type aliases
+fail_compilation/fail21243.d(18): Error: semicolon expected to close `alias` declaration
+fail_compilation/fail21243.d(18): Error: declaration expected, not `=>`
+fail_compilation/fail21243.d(19): Error: `auto` can only be used as part of `auto ref` for function literal return values
+---
++/
+auto a = auto (int x) => x;
+auto b = function auto (int x) { return x; };
+alias c = auto (int x) => x;
+alias d = function auto (int x) { return x; };
index 91b4e79ad2ddc3777c009c0bf78526a3f1c48c46..5c5c11b719535cbcfe00c0762d7008c6811b0f9f 100644 (file)
@@ -4,8 +4,8 @@ EXTRA_FILES: imports/test23109a.d imports/test23109b.d imports/test23109c.d
 EXTRA_SOURCES: extra-files/test23109/object.d
 TEST_OUTPUT:
 ---
-Error: no property `getHash` for type `object.TypeInfo_Const`
-Error: no property `getHash` for type `object.TypeInfo_Const`
+Error: no property `getHash` for `typeid(const(Ensure[]))` of type `object.TypeInfo_Const`
+Error: no property `getHash` for `typeid(const(Ensure[1]))` of type `object.TypeInfo_Const`
 fail_compilation/imports/test23109a.d(10): Error: template instance `imports.test23109a.Array!(Ensure)` error instantiating
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7372.d b/gcc/testsuite/gdc.test/fail_compilation/fail7372.d
new file mode 100644 (file)
index 0000000..2d56e09
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/imports/fail7372.d(7): Error: undefined identifier `X`
+fail_compilation/fail7372.d(4):        parent scope from here: `mixin Issue7372!()`
+---
+*/
+#line 1
+import imports.fail7372;
+interface I {}
+class C : I {
+    mixin Issue7372!();
+}
index d7853e6b3461c21b555cfb210a10945f682a2e8f..c44b289cceca5d7a1f90c2d464cf30e6ef25ba17 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/faildottypeinfo.d(11): Error: no property `typeinfo` for type `int`
+fail_compilation/faildottypeinfo.d(11): Error: no property `typeinfo` for `0` of type `int`
 fail_compilation/faildottypeinfo.d(12): Error: no property `typeinfo` for type `object.Object`
 ---
 */
index bbec698a740bb843cd3b0f3530ca27fa83942243..ff0d26be6737f67d2856cbcbabbe315fc97a6fdf 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/failoffset.d(12): Error: no property `offset` for type `int`
+fail_compilation/failoffset.d(12): Error: no property `offset` for `b` of type `int`
 fail_compilation/failoffset.d(12):        while evaluating: `static assert(b.offset == 4)`
 ---
 */
index 2084e32d6b8b8d59018619ddef29068de6145ca3..d21ee47dec749fd590185ae7b64ecc2c19d4ba04 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice10938.d(13): Error: no property `opts` for type `ice10938.C`
+fail_compilation/ice10938.d(13): Error: no property `opts` for `this` of type `ice10938.C`
 fail_compilation/ice10938.d(13):        potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
 ---
 */
index 019722a063e72ce37237e329af4b8a5c50166610..dbe386e97e996d1f129fe2893c574c87db7f4ff1 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice12174.d(12): Error: no property `sum` for type `int[]`
+fail_compilation/ice12174.d(12): Error: no property `sum` for `[1, 2, 3]` of type `int[]`
 fail_compilation/ice12174.d(20): Error: CTFE failed because of previous errors in `this`
 fail_compilation/ice12174.d(13):        called from here: `filter([1, 2, 3])`
 ---
index e800838c45c14becfbd2b28920fd8bd237a1c5fa..b26fe4cda046f740de7c1417d28ac8dd1dcb13e5 100644 (file)
@@ -2,19 +2,21 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice15855.d(25): Error: found `End of File` when expecting `(`
-fail_compilation/ice15855.d(25): Error: found `End of File` instead of statement
-fail_compilation/ice15855.d(25): Error: expression expected, not `End of File`
-fail_compilation/ice15855.d(25): Error: found `End of File` when expecting `;` following `for` condition
-fail_compilation/ice15855.d(25): Error: expression expected, not `End of File`
-fail_compilation/ice15855.d(25): Error: found `End of File` when expecting `)`
-fail_compilation/ice15855.d(25): Error: found `End of File` instead of statement
-fail_compilation/ice15855.d(25): Error: found `End of File` when expecting `}` following compound statement
-fail_compilation/ice15855.d(25): Error: found `End of File` when expecting `]`
-fail_compilation/ice15855.d(25): Error: no identifier for declarator `a[()
+fail_compilation/ice15855.d(27): Error: found `End of File` when expecting `(`
+fail_compilation/ice15855.d(27): Error: found `End of File` instead of statement
+fail_compilation/ice15855.d(27): Error: expression expected, not `End of File`
+fail_compilation/ice15855.d(27): Error: found `End of File` when expecting `;` following `for` condition
+fail_compilation/ice15855.d(27): Error: expression expected, not `End of File`
+fail_compilation/ice15855.d(27): Error: found `End of File` when expecting `)`
+fail_compilation/ice15855.d(27): Error: found `End of File` instead of statement
+fail_compilation/ice15855.d(27): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/ice15855.d(27): Error: found `End of File` when expecting `]`
+fail_compilation/ice15855.d(27): Error: no identifier for declarator `a[()
 {
-for (; 0; 0)
+for (__error__
+ 0; 0)
 {
+__error__
 }
 }
 ]`
index 8803956f6c592ec0729aff4d245da1a173d7a6c3..796dd3dcc9decdb2f70635523633cd8ff93ba065 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice18469.d(10): Error: no property `opCall` for type `void`
+fail_compilation/ice18469.d(10): Error: no property `opCall` for `this.~this()` of type `void`
 ---
 */
 class Bar
index f94847778318201b171148c2b99a994504c3abea..6d60fc43c1b2724d13c706201c2da13c0435bca5 100644 (file)
@@ -1,6 +1,6 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/ice19755.d(11): Error: no property `x` for type `ice19755.Thunk!int*`
+fail_compilation/ice19755.d(11): Error: no property `x` for `self` of type `ice19755.Thunk!int*`
 fail_compilation/ice19755.d(16): Error: template instance `ice19755.Thunk!int` error instantiating
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/fail7372.d b/gcc/testsuite/gdc.test/fail_compilation/imports/fail7372.d
new file mode 100644 (file)
index 0000000..f71c736
--- /dev/null
@@ -0,0 +1,9 @@
+module imports.fail7372;
+import imports.imp1;
+mixin template Issue7372()
+{
+    public void f()
+    {
+        int foo = X;
+    }
+}
index b50a616ac974bd40c3f956e017ca316a95df417a..11fddf069d6df4457b88ce4bbcdd713c505ade64 100644 (file)
@@ -22,7 +22,6 @@ fail_compilation/misc_parser_err_cov1.d(39): Error: expression expected, not `;`
 fail_compilation/misc_parser_err_cov1.d(40): Error: semicolon expected following auto declaration, not `auto`
 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: found `.` when expecting `;` following statement
 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(43): Error: found `End of File` when expecting `}` following compound statement
diff --git a/gcc/testsuite/gdc.test/fail_compilation/mixinprop.d b/gcc/testsuite/gdc.test/fail_compilation/mixinprop.d
new file mode 100644 (file)
index 0000000..db8bf59
--- /dev/null
@@ -0,0 +1,13 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/mixinprop.d(12): Error: no property `x` for `mixin Foo!() F;
+` of type `void`
+---
+*/
+mixin template Foo() { }
+
+void main()
+{
+    mixin Foo F;
+    F.x;
+}
index 23a3660dc9ae6795bf4898ff2557fe826baeda0f..594b5d35e758bd184f4b5c7ddb44f9327c0d0d10 100644 (file)
@@ -2,7 +2,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test15785.d(16): Error: no property `foo` for type `imports.test15785.Base`
+fail_compilation/test15785.d(16): Error: no property `foo` for `super` of type `imports.test15785.Base`
 fail_compilation/test15785.d(17): Error: undefined identifier `bar`
 ---
 */
index e4ade7de2a41292123dbba473a7585f0f7f0df40..db554cb9e15a1610bdbcd8a65bff1b6836bb7304 100644 (file)
@@ -3,7 +3,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test15897.d(19): Error: no property `create` for type `imports.test15897.Cat`
+fail_compilation/test15897.d(19): Error: no property `create` for `cat` of type `imports.test15897.Cat`
 ---
 */
 module test15897;
index c4a0fa63e75ef528801395235d14b923b935ab2c..bdaae94a6ca3ae273fbd65865cc8953cee974bdf 100644 (file)
@@ -1,7 +1,7 @@
 /* REQUIRED_ARGS: -preview=bitfields
  * TEST_OUTPUT:
 ---
-fail_compilation/test16188.d(101): Error: no property `name` for type `test16188.Where`
+fail_compilation/test16188.d(101): Error: no property `name` for `Where()` of type `test16188.Where`
 fail_compilation/test16188.d(101):        potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
 ---
  */
index 2456a590c578fd9eb6086f9cbbef57717e3d3d96..f523337037af5267c8e2b3223182ae191d8214ea 100644 (file)
@@ -2,7 +2,7 @@
 TEST_OUTPUT:
 ---
 (spec:1) fail_compilation/test17380spec.d(14): Error: cannot resolve identifier `ThisTypeDoesNotExistAndCrashesTheCompiler`
-(spec:1) fail_compilation/test17380spec.d(14): Error: no property `ThisTypeDoesNotExistAndCrashesTheCompiler` for type `test17380spec.Uint128`
+(spec:1) fail_compilation/test17380spec.d(14): Error: no property `ThisTypeDoesNotExistAndCrashesTheCompiler` for `this.opCast()` of type `test17380spec.Uint128`
 fail_compilation/test17380spec.d(14): Error: undefined identifier `ThisTypeDoesNotExistAndCrashesTheCompiler`
 ---
  */
index e32ad9c9d74eaddb3002f56ca3c28ec38bc2e32f..302eb3da31a132ae18cc521f0587bcacb89871b2 100644 (file)
@@ -3,10 +3,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test21096.d(13): Error: identifier or new keyword expected following `(...)`.
-fail_compilation/test21096.d(13): Error: found `.` when expecting `]`
-fail_compilation/test21096.d(13): Error: no identifier for declarator `char`
-fail_compilation/test21096.d(13): Error: declaration expected, not `]`
+fail_compilation/test21096.d(11): Error: identifier or new keyword expected following `(...)`.
+fail_compilation/test21096.d(11): Error: no identifier for declarator `char[(__error)]`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22680.d b/gcc/testsuite/gdc.test/fail_compilation/test22680.d
new file mode 100644 (file)
index 0000000..caf0f4a
--- /dev/null
@@ -0,0 +1,17 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test22680.d(104): Error: scope variable `this` assigned to non-scope `c`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=22680
+
+#line 100
+
+C c;
+class C {
+    ~this() @safe {
+           c = this;
+    }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/newaa.d b/gcc/testsuite/gdc.test/runnable/newaa.d
new file mode 100644 (file)
index 0000000..94e79d5
--- /dev/null
@@ -0,0 +1,23 @@
+void main()
+{
+    alias AA = int[string];
+    // aa is not ref
+    static void test(AA aa)
+    {
+        aa[""] = 0;
+    }
+    auto aa = new AA();
+    auto ab = new int[string];
+    auto ac = new typeof(aa);
+    test(aa);
+    test(ab);
+    test(ac);
+    assert(aa.length);
+    assert(ab.length);
+    assert(ac.length);
+
+    int[string] a = new int[string];
+    auto b = a;
+    a["seven"] = 7;
+    assert(b["seven"] == 7);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test23234.d b/gcc/testsuite/gdc.test/runnable/test23234.d
new file mode 100644 (file)
index 0000000..7872aa7
--- /dev/null
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=23234
+
+class Bar
+{
+}
+
+class Foo
+{
+    Bar get() { return new Bar; }
+    alias get this;
+}
+
+void main()
+{
+    auto foo = new Foo;
+    void test(Bar delegate() dg)
+    {
+        assert(dg() !is null);
+    }
+
+    test(() => foo);
+}
index f47d2b2bd9755389cbf99fcb36c521dcac652ae5..586aea80ccef258b2891116bfc0c6a5da9f8095d 100644 (file)
@@ -230,6 +230,21 @@ void test5()
     static assert(!__traits(compiles, s.err += 1));
 }
 
+void test6()
+{
+    int dtors;
+    struct S6
+    {
+        @disable this(this);
+        ~this() { dtors++; }
+    }
+
+    S6[2] arr;
+    arr = S6();
+
+    assert(dtors == 2);
+}
+
 /***************************************************/
 // https://issues.dlang.org/show_bug.cgi?id=4424
 
@@ -1192,6 +1207,7 @@ int main()
     test3();
     test4();
     test5();
+    test6();
     test4424();
     test6174a();
     test6174b();
index 85fc49d3d0a2e8f62f298079c618beec2a09b803..a4c46f3306ede57b9563edfe93b88cb6026ed42b 100644 (file)
@@ -1,4 +1,4 @@
-817610b16d0f0f469b9fbb28c000956fb910c43f
+4219ba670ce9ff92f3e874f0f048f2c28134c008
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
index d828749837ad89e1d0866638ed92652e6de87474..45749d7ef103b1a4b60d6a2bfc40e20fafb88d7b 100644 (file)
@@ -211,11 +211,11 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
        gcc/sections/package.d gcc/sections/pecoff.d gcc/simd.d \
        gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
        gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
-       rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arrayassign.d \
-       rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d rt/deh.d \
-       rt/dmain2.d rt/ehalloc.d rt/invariant.d rt/lifetime.d rt/memory.d \
-       rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d rt/tlsgc.d \
-       rt/util/typeinfo.d rt/util/utility.d
+       rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
+       rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
+       rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
+       rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
+       rt/util/utility.d
 
 DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
        core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
index 57660eeed1a4e2e2bae9fbbf0356d4a34ea7f00c..e86721fb3feeaecf5db75b434c7de7c9d02d2c2f 100644 (file)
@@ -242,11 +242,11 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
        gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
        gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
        object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
-       rt/arrayassign.lo rt/arraycat.lo rt/cast_.lo rt/config.lo \
-       rt/critical_.lo rt/deh.lo rt/dmain2.lo rt/ehalloc.lo \
-       rt/invariant.lo rt/lifetime.lo rt/memory.lo rt/minfo.lo \
-       rt/monitor_.lo rt/profilegc.lo rt/sections.lo rt/tlsgc.lo \
-       rt/util/typeinfo.lo rt/util/utility.lo
+       rt/arraycat.lo rt/cast_.lo rt/config.lo rt/critical_.lo \
+       rt/deh.lo rt/dmain2.lo rt/ehalloc.lo rt/invariant.lo \
+       rt/lifetime.lo rt/memory.lo rt/minfo.lo rt/monitor_.lo \
+       rt/profilegc.lo rt/sections.lo rt/tlsgc.lo rt/util/typeinfo.lo \
+       rt/util/utility.lo
 am__objects_2 = core/stdc/libgdruntime_la-errno_.lo
 am__objects_3 = core/sys/elf/package.lo
 am__objects_4 = core/stdcpp/allocator.lo core/stdcpp/array.lo \
@@ -880,11 +880,11 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
        gcc/sections/package.d gcc/sections/pecoff.d gcc/simd.d \
        gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
        gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
-       rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arrayassign.d \
-       rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d rt/deh.d \
-       rt/dmain2.d rt/ehalloc.d rt/invariant.d rt/lifetime.d rt/memory.d \
-       rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d rt/tlsgc.d \
-       rt/util/typeinfo.d rt/util/utility.d
+       rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
+       rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
+       rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
+       rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
+       rt/util/utility.d
 
 DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
        core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
@@ -1364,7 +1364,6 @@ rt/aApply.lo: rt/$(am__dirstamp)
 rt/aApplyR.lo: rt/$(am__dirstamp)
 rt/aaA.lo: rt/$(am__dirstamp)
 rt/adi.lo: rt/$(am__dirstamp)
-rt/arrayassign.lo: rt/$(am__dirstamp)
 rt/arraycat.lo: rt/$(am__dirstamp)
 rt/cast_.lo: rt/$(am__dirstamp)
 rt/config.lo: rt/$(am__dirstamp)
index cf05333429ed36653935bfaf49c1f8f0fa67902e..1c49035890471e6cbe3d0aa6a58e39fb5bf1c09a 100644 (file)
@@ -15,7 +15,7 @@ module __builtins;
 /* gcc relies on internal __builtin_xxxx functions and templates to
  * accomplish <stdarg.h>. D does the same thing with templates in core.stdc.stdarg.
  * Here, we redirect the gcc builtin declarations to the equivalent
- * ones in core.stdc.stdarg, thereby avoiding having to hardware them
+ * ones in core.stdc.stdarg, thereby avoiding having to hardwire them
  * into the D compiler.
  */
 
index ca87f902909e7eaca98d92a09792891491dd9ad7..c2d40322961d338b8328def9c5e42a17d5dac61b 100644 (file)
@@ -82,7 +82,7 @@ pure @safe:
 
     static class ParseException : Exception
     {
-        @safe pure nothrow this( string msg )
+        this(string msg) @safe pure nothrow
         {
             super( msg );
         }
@@ -91,14 +91,14 @@ pure @safe:
 
     static class OverflowException : Exception
     {
-        @safe pure nothrow this( string msg )
+        this(string msg) @safe pure nothrow
         {
             super( msg );
         }
     }
 
 
-    static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
+    static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
     {
         pragma(inline, false); // tame dmd inliner
 
@@ -110,7 +110,7 @@ pure @safe:
     }
 
 
-    static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
+    static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
     {
         pragma(inline, false); // tame dmd inliner
 
@@ -156,7 +156,6 @@ pure @safe:
         if (val >= '0' && val <= '9')
             return cast(ubyte)(val - '0');
         error();
-        return 0;
     }
 
 
@@ -253,23 +252,22 @@ pure @safe:
             put(", ");
     }
 
-    char[] put(char c) return scope
+    void put(char c) return scope
     {
         char[1] val = c;
-        return put(val[]);
+        put(val[]);
     }
 
-    char[] put( scope const(char)[] val ) return scope
+    void put(scope const(char)[] val) return scope
     {
         pragma(inline, false); // tame dmd inliner
 
-        if ( val.length )
-        {
-            if ( !contains( dst[0 .. len], val ) )
-                return append( val );
-            return shift( val );
-        }
-        return null;
+        if (!val.length) return;
+
+        if (!contains(dst[0 .. len], val))
+            append(val);
+        else
+            shift(val);
     }
 
 
@@ -948,18 +946,19 @@ pure @safe:
             return dst[beg .. len];
         case 'D': // TypeDelegate (D TypeFunction)
             popFront();
-            auto modbeg = len;
-            parseModifier();
-            auto modend = len;
+            auto modifiers = parseModifier();
             if ( front == 'Q' )
                 parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) );
             else
                 parseTypeFunction( name, IsDelegate.yes );
-            if (modend > modbeg)
+            if (modifiers)
             {
-                // move modifiers behind the function arguments
-                shift(dst[modend-1 .. modend]); // trailing space
-                shift(dst[modbeg .. modend-1]);
+                // write modifiers behind the function arguments
+                while (auto str = typeCtors.toStringConsume(modifiers))
+                {
+                    put(' ');
+                    put(str);
+                }
             }
             return dst[beg .. len];
         case 'n': // TypeNone (n)
@@ -1009,7 +1008,6 @@ pure @safe:
                 }
             }
             error();
-            return null;
         }
     }
 
@@ -1110,43 +1108,44 @@ pure @safe:
         }
     }
 
-    void parseModifier()
+    /// Returns: Flags of `TypeCtor`
+    ushort parseModifier()
     {
+        TypeCtor res = TypeCtor.None;
         switch ( front )
         {
         case 'y':
             popFront();
-            put( "immutable " );
-            break;
+            return TypeCtor.Immutable;
         case 'O':
             popFront();
-            put( "shared " );
-            if ( front == 'x' )
+            res |= TypeCtor.Shared;
+            if (front == 'x')
                 goto case 'x';
-            if ( front == 'N' )
+            if (front == 'N')
                 goto case 'N';
-            break;
+            return TypeCtor.Shared;
         case 'N':
-            if ( peek( 1 ) != 'g' )
-                break;
+            if (peek( 1 ) != 'g')
+                return res;
             popFront();
             popFront();
-            put( "inout " );
+            res |= TypeCtor.InOut;
             if ( front == 'x' )
                 goto case 'x';
-            break;
+            return res;
         case 'x':
             popFront();
-            put( "const " );
-            break;
-        default: break;
+            res |= TypeCtor.Const;
+            return res;
+        default: return TypeCtor.None;
         }
     }
 
-    void parseFuncAttr()
+    ushort parseFuncAttr()
     {
         // FuncAttrs
-        breakFuncAttrs:
+        ushort result;
         while ('N' == front)
         {
             popFront();
@@ -1154,27 +1153,27 @@ pure @safe:
             {
             case 'a': // FuncAttrPure
                 popFront();
-                put( "pure " );
+                result |= FuncAttributes.Pure;
                 continue;
             case 'b': // FuncAttrNoThrow
                 popFront();
-                put( "nothrow " );
+                result |= FuncAttributes.Nothrow;
                 continue;
             case 'c': // FuncAttrRef
                 popFront();
-                put( "ref " );
+                result |= FuncAttributes.Ref;
                 continue;
             case 'd': // FuncAttrProperty
                 popFront();
-                put( "@property " );
+                result |= FuncAttributes.Property;
                 continue;
             case 'e': // FuncAttrTrusted
                 popFront();
-                put( "@trusted " );
+                result |= FuncAttributes.Trusted;
                 continue;
             case 'f': // FuncAttrSafe
                 popFront();
-                put( "@safe " );
+                result |= FuncAttributes.Safe;
                 continue;
             case 'g':
             case 'h':
@@ -1188,27 +1187,42 @@ pure @safe:
                 //       if we see these, then we know we're really in
                 //       the parameter list.  Rewind and break.
                 pos--;
-                break breakFuncAttrs;
+                return result;
             case 'i': // FuncAttrNogc
                 popFront();
-                put( "@nogc " );
+                result |= FuncAttributes.NoGC;
                 continue;
             case 'j': // FuncAttrReturn
                 popFront();
-                put( "return " );
+                if (this.peek(0) == 'N' && this.peek(1) == 'l')
+                {
+                    result |= FuncAttributes.ReturnScope;
+                    popFront();
+                    popFront();
+                } else {
+                    result |= FuncAttributes.Return;
+                }
                 continue;
             case 'l': // FuncAttrScope
                 popFront();
-                put( "scope " );
+                if (this.peek(0) == 'N' && this.peek(1) == 'j')
+                {
+                    result |= FuncAttributes.ScopeReturn;
+                    popFront();
+                    popFront();
+                } else {
+                    result |= FuncAttributes.Scope;
+                }
                 continue;
             case 'm': // FuncAttrLive
                 popFront();
-                put( "@live " );
+                result |= FuncAttributes.Live;
                 continue;
             default:
                 error();
             }
         }
+        return result;
     }
 
     void parseFuncArguments() scope
@@ -1346,19 +1360,20 @@ pure @safe:
         auto beg = len;
 
         parseCallConvention();
-        auto attrbeg = len;
-        parseFuncAttr();
+        auto attributes = parseFuncAttr();
 
         auto argbeg = len;
         put( '(' );
         parseFuncArguments();
         put( ')' );
-        if (attrbeg < argbeg)
+        if (attributes)
         {
-            // move function attributes behind arguments
-            shift( dst[argbeg - 1 .. argbeg] ); // trailing space
-            shift( dst[attrbeg .. argbeg - 1] ); // attributes
-            argbeg = attrbeg;
+            // write function attributes behind arguments
+            while (auto str = funcAttrs.toStringConsume(attributes))
+            {
+                put(' ');
+                put(str);
+            }
         }
         auto retbeg = len;
         parseType();
@@ -1900,21 +1915,26 @@ pure @safe:
             {
                 // do not emit "needs this"
                 popFront();
-                parseModifier();
+                auto modifiers = parseModifier();
+                while (auto str = typeCtors.toStringConsume(modifiers))
+                {
+                    put(str);
+                    put(' ');
+                }
             }
             if ( isCallConvention( front ) )
             {
                 // we don't want calling convention and attributes in the qualified name
                 parseCallConvention();
-                parseFuncAttr();
-                if ( keepAttr )
-                {
+                auto attributes = parseFuncAttr();
+                if (keepAttr) {
+                    while (auto str = funcAttrs.toStringConsume(attributes))
+                    {
+                        put(str);
+                        put(' ');
+                    }
                     attr = dst[prevlen .. len];
                 }
-                else
-                {
-                    len = prevlen;
-                }
 
                 put( '(' );
                 parseFuncArguments();
@@ -2637,6 +2657,12 @@ else
         ["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"],
         ["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"],
         ["_D4test4rrs1FNkMPiZv",  "void test.rrs1(return scope int*)"],
+
+        // `scope` and `return` combinations
+        ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"],
+        ["_D3foo3FooQiMNgFNlNfZv",        "inout scope @safe void foo.Foo.foo()"],
+        ["_D3foo3Foo4foorMNgFNjNfZv",     "inout return @safe void foo.Foo.foor()"],
+        ["_D3foo3Foo3rabMNgFNlNjNfZv",    "inout scope return @safe void foo.Foo.rab()"],
     ];
 
 
@@ -2720,7 +2746,12 @@ unittest
 }
 
 /*
+ * Expand an OMF, DMD-generated compressed identifier into its full form
  *
+ * This function only has a visible effect for OMF binaries (Win32),
+ * as compression is otherwise not used.
+ *
+ * See_Also: `compiler/src/dmd/backend/compress.d`
  */
 string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
 {
@@ -2781,3 +2812,92 @@ extern (C) private
         errno = err;
     }
 }
+
+private struct ManglingFlagInfo
+{
+    /// The flag value to use
+    ushort flag;
+
+    /// Human-readable representation
+    string value;
+}
+
+private enum TypeCtor : ushort {
+    None      = 0,
+    //// 'x'
+    Const     = (1 << 1),
+    /// 'y'
+    Immutable = (1 << 2),
+    /// 'O'
+    Shared    = (1 << 3),
+    ///
+    InOut     = (1 << 4),
+}
+
+private immutable ManglingFlagInfo[] typeCtors = [
+    ManglingFlagInfo(TypeCtor.Immutable, "immutable"),
+    ManglingFlagInfo(TypeCtor.Shared,    "shared"),
+    ManglingFlagInfo(TypeCtor.InOut,     "inout"),
+    ManglingFlagInfo(TypeCtor.Const,     "const"),
+];
+
+private enum FuncAttributes : ushort {
+    None      = 0,
+    //// 'a'
+    Pure     = (1 << 1),
+    //// 'b'
+    Nothrow  = (1 << 2),
+    //// 'c'
+    Ref      = (1 << 3),
+    //// 'd'
+    Property = (1 << 4),
+    //// 'e'
+    Trusted  = (1 << 5),
+    //// 'f'
+    Safe     = (1 << 6),
+    //// 'i'
+    NoGC     = (1 << 7),
+    //// 'j'
+    Return   = (1 << 8),
+    //// 'l'
+    Scope    = (1 << 9),
+    //// 'm'
+    Live     = (1 << 10),
+
+    /// Their order matter
+    ReturnScope   = (1 << 11),
+    ScopeReturn   = (1 << 12),
+}
+
+// The order in which we process is the same as in compiler/dmd/src/dmangle.d
+private immutable ManglingFlagInfo[] funcAttrs = [
+    ManglingFlagInfo(FuncAttributes.Pure,     "pure"),
+    ManglingFlagInfo(FuncAttributes.Nothrow,  "nothrow"),
+    ManglingFlagInfo(FuncAttributes.Ref,      "ref"),
+    ManglingFlagInfo(FuncAttributes.Property, "@property"),
+    ManglingFlagInfo(FuncAttributes.NoGC,     "@nogc"),
+
+    ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"),
+    ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"),
+
+    ManglingFlagInfo(FuncAttributes.Return,   "return"),
+    ManglingFlagInfo(FuncAttributes.Scope,    "scope"),
+
+    ManglingFlagInfo(FuncAttributes.Live,     "@live"),
+    ManglingFlagInfo(FuncAttributes.Trusted,  "@trusted"),
+    ManglingFlagInfo(FuncAttributes.Safe,     "@safe"),
+];
+
+private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base)
+    @safe pure nothrow @nogc
+{
+    foreach (const ref info; infos)
+    {
+        if ((base & info.flag) == info.flag)
+        {
+            base &= ~info.flag;
+            return info.value;
+        }
+    }
+    return null;
+}
index 6132e68db1a965c4bb80c60943587c0f3224f67c..6e3c1fdc3effd8389190f68ed13d13cfe2c31a2f 100644 (file)
@@ -302,3 +302,151 @@ Tarr _d_arrayassign_r(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @tru
     assert(!didThrow);
     assert(counter == 0);
 }
+
+/**
+ * Sets all elements of an array to a single value. Takes into account postblits,
+ * copy constructors and destructors. For Plain Old Data elements,`rt/memset.d`
+ * is used.
+ *
+ * ---
+ * struct S
+ * {
+ *     ~this() {} // destructor, so not Plain Old Data
+ * }
+ *
+ * void main()
+ * {
+ *   S[3] arr;
+ *   S value;
+ *
+ *   arr = value;
+ *   // Generates:
+ *   // _d_arraysetassign(arr[], value), arr;
+ * }
+ * ---
+ *
+ * Params:
+ *     to = destination array
+ *     value = the element to set
+ * Returns:
+ *     `to`
+ */
+Tarr _d_arraysetassign(Tarr : T[], T)(return scope Tarr to, scope ref T value) @trusted
+{
+    import core.internal.traits : Unqual;
+    import core.lifetime : copyEmplace;
+    import core.stdc.string : memcpy;
+
+    enum elemSize = T.sizeof;
+    void[elemSize] tmp = void;
+
+    foreach (ref dst; to)
+    {
+        memcpy(&tmp, cast(void*) &dst, elemSize);
+        // Use `memcpy` if `T` has a `@disable`d postblit.
+        static if (__traits(isCopyable, T))
+            copyEmplace(value, dst);
+        else
+            memcpy(cast(void*) &value, cast(void*) &dst, elemSize);
+        auto elem = cast(Unqual!T*) &tmp;
+        destroy(*elem);
+    }
+
+    return to;
+}
+
+// postblit and destructor
+@safe unittest
+{
+    string ops;
+    struct S
+    {
+        int val;
+        this(this) { ops ~= "="; }
+        ~this() { ops ~= "~"; }
+    }
+
+    S[4] arr;
+    S s = S(1234);
+    _d_arraysetassign(arr[], s);
+    assert(ops == "=~=~=~=~");
+    assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
+}
+
+// copy constructor
+@safe unittest
+{
+    string ops;
+    struct S
+    {
+        int val;
+        this(const scope ref S rhs)
+        {
+            val = rhs.val;
+            ops ~= "=";
+        }
+        ~this() { ops ~= "~"; }
+    }
+
+    S[4] arr;
+    S s = S(1234);
+    _d_arraysetassign(arr[], s);
+    assert(ops == "=~=~=~=~");
+    assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
+}
+
+// throwing and `nothrow`
+@safe nothrow unittest
+{
+    // Test that throwing works
+    bool didThrow;
+    int counter;
+    struct Throw
+    {
+        int val;
+        this(this)
+        {
+            counter++;
+            if (counter == 2)
+                throw new Exception("Oh no.");
+        }
+    }
+
+    try
+    {
+        Throw[4] a;
+        Throw b = Throw(1);
+        _d_arraysetassign(a[], b);
+    }
+    catch (Exception)
+    {
+        didThrow = true;
+    }
+    assert(didThrow);
+    assert(counter == 2);
+
+    // Test that `nothrow` works
+    didThrow = false;
+    counter = 0;
+    struct NoThrow
+    {
+        int val;
+        this(this) { counter++; }
+    }
+
+    try
+    {
+        NoThrow[4] a;
+        NoThrow b = NoThrow(1);
+        _d_arraysetassign(a[], b);
+        foreach (ref e; a)
+            assert(e == NoThrow(1));
+    }
+    catch (Exception)
+    {
+        didThrow = true;
+    }
+    assert(!didThrow);
+    // The array `a` is destroyed when the `try` block ends.
+    assert(counter == 4);
+}
index 07486c216f0b7e4905af9dfd7ac7aa092acd1672..dbad0e6064f22e38dcb72db84ec2c4c63f635fa1 100644 (file)
@@ -518,7 +518,7 @@ private auto assumeFakeAttributes(T)(T t) @trusted
 }
 
 /// Wrapper for `miniFormat` which assumes that the implementation is `@safe`, `@nogc`, ...
-/// s.t. it does not violate the constraints of the the function containing the `assert`.
+/// s.t. it does not violate the constraints of the function containing the `assert`.
 private string miniFormatFakeAttributes(T)(const scope ref T t)
 {
     alias miniT = miniFormat!T;
index 27bf7f2b90552ca1eb28e12314b8d105c0c703a7..9808b9947f5de3034fe8f8465b9fece73bd4d60d 100644 (file)
@@ -567,17 +567,22 @@ ubyte codeLength(C)(dchar c)
 
 /***********************************
 Checks to see if string is well formed or not. $(D S) can be an array
- of $(D char), $(D wchar), or $(D dchar). Throws a $(D UtfException)
if it is not. Use to check all untrusted input for correctness.
+ of $(D char), $(D wchar), or $(D dchar). Returns $(D false) if it is not.
+ Use to check all untrusted input for correctness.
  */
-@safe pure
-void validate(S)(const scope S s)
+@safe pure nothrow
+bool isValidString(S)(const scope S s)
 {
     auto len = s.length;
     for (size_t i = 0; i < len; )
     {
-        decode(s, i);
+        try
+            decode(s, i);
+        catch (Exception e)
+            return false;
     }
+
+    return true;
 }
 
 /* =================== Conversion to UTF8 ======================= */
@@ -626,7 +631,7 @@ char[] toUTF8(return scope char[] buf, dchar c)
 string toUTF8(return scope string s)
     in
     {
-        validate(s);
+        assert(isValidString(s));
     }
     do
     {
@@ -787,7 +792,7 @@ wptr toUTF16z(const scope char[] s)
 wstring toUTF16(return scope wstring s)
     in
     {
-        validate(s);
+        assert(isValidString(s));
     }
     do
     {
@@ -867,7 +872,7 @@ dstring toUTF32(const scope wchar[] s)
 dstring toUTF32(return scope dstring s)
     in
     {
-        validate(s);
+        assert(isValidString(s));
     }
     do
     {
index f1bf59b0ac49f6da0b35c0309e53948e11e06994..c2b343a08475dfec4e8c9dc6e9e6ae9b018da135 100644 (file)
@@ -704,7 +704,7 @@ version (D_SIMD)
                 // store `v` to location pointed to by `d`
                 storeUnaligned(cast(T*)d, v);
 
-                // check that the the data was stored correctly
+                // check that the data was stored correctly
                 foreach (j; 0..T.sizeof)
                 {
                     assert(ptrToV[j] == d[j]);
index 88123fb16a64f4a95cbca66f7f492f000dd52e00..5242ba9d4e21d10858a6dbd826688f3060f045b5 100644 (file)
@@ -483,7 +483,7 @@ else version (CRuntime_UClibc)
 
         alias fexcept_t = ushort;
     }
-    else version (MIPS32)
+    else version (MIPS_Any)
     {
         struct fenv_t
         {
index 0393ea52c072b7968888c455fe06e64f552502a2..2666c952992568c0915d8f1e44eebd89b3753db3 100644 (file)
@@ -106,21 +106,14 @@ else version (CRuntime_Bionic)
 }
 else version (CRuntime_UClibc)
 {
-    version (X86)
-    {
-        ///
-        enum int FP_ILOGB0        = int.min;
-        ///
-        enum int FP_ILOGBNAN      = int.min;
-    }
-    else version (X86_64)
+    version (X86_Any)
     {
         ///
         enum int FP_ILOGB0        = int.min;
         ///
         enum int FP_ILOGBNAN      = int.min;
     }
-    else version (MIPS32)
+    else version (MIPS_Any)
     {
         ///
         enum int FP_ILOGB0        = -int.max;
index f46698c3b5317edf68b43bc3b2ea4f65fd6ef665..7713eea97858d7a77933001b90b9d533e4794b6c 100644 (file)
@@ -1803,7 +1803,7 @@ version (CoreDdoc)
      * image. The isub_image field is an index into the sub-images
      * (sub-frameworks and sub-umbrellas list) that made up the two-level image
      * that the undefined symbol was found in when it was built by the static
-     * link editor. If isub-image is 0 the the symbol is expected to be defined
+     * link editor. If isub-image is 0 the symbol is expected to be defined
      * in library and not in the sub-images. If isub-image is non-zero it is an
      * index into the array of sub-images for the umbrella with the first index
      * in the sub-images being 1. The array of sub-images is the ordered list of
index 11c374530bfdf3e646ed256cb9ab139ddec6a0e1..89dc019cc8f2a82fdb2671bc1f5d95d71a441549 100644 (file)
@@ -42,7 +42,7 @@ These cmd values will set locks that conflict with process-associated
 record  locks, but are "owned" by the open file description, not the
 process. This means that they are inherited across fork() like BSD (flock)
 locks, and they are only released automatically when the last reference to
-the the open file against which they were acquired is put.
+the open file against which they were acquired is put.
 
 */
 enum
index a9519ca234aa9594bd984186644892da50cf0f2e..04a8e8bb8668481656abec8b331b4f5df7cb5304 100644 (file)
@@ -377,7 +377,7 @@ else version (CRuntime_Musl)
 }
 else version (CRuntime_UClibc)
 {
-    version (X86_64)
+    version (X86_Any)
     {
         enum RTLD_LAZY              = 0x0001;
         enum RTLD_NOW               = 0x0002;
@@ -387,7 +387,7 @@ else version (CRuntime_UClibc)
         enum RTLD_LOCAL             = 0;
         enum RTLD_NODELETE          = 0x01000;
     }
-    else version (MIPS32)
+    else version (MIPS_Any)
     {
         enum RTLD_LAZY              = 0x0001;
         enum RTLD_NOW               = 0x0002;
index 2f1a8c69ad692d37b2b12de1f77dc51628f3e040..7085fc4dfa54f99a73f6a139fc269af811e55b98 100644 (file)
@@ -147,7 +147,7 @@ int mq_notify (mqd_t mqdes, const(sigevent)* notification);
 
 
 /**
- * Receive the oldest message with the highest priority the the message queue
+ * Receive the oldest message with the highest priority the message queue
  *
  * Params:
  *   mqdes      = Message queue descriptor.
@@ -164,7 +164,7 @@ ssize_t mq_receive (mqd_t mqdes, char* msg_ptr, size_t msg_len, uint* msg_prio);
 
 
 /**
- * Receive the oldest message with the highest priority the the message queue,
+ * Receive the oldest message with the highest priority the message queue,
  * wait up to a certain timeout.
  *
  * Params:
index 91e3a19d081ea300ed0c13f215a9581e5f9f7a3f..5a15d82d2ee85bf356de6cd137778cf5161376c3 100644 (file)
@@ -370,6 +370,22 @@ else version (CRuntime_UClibc)
                 double[6] __fpregs;
         }
     }
+    else version (MIPS64)
+    {
+        struct __jmp_buf
+        {
+            long __pc;
+            long __sp;
+            long[8] __regs;
+            long __fp;
+            long __gp;
+            int __fpc_csr;
+            version (MIPS_N64)
+                double[8] __fpregs;
+            else
+                double[6] __fpregs;
+        }
+    }
     else
         static assert(0, "unimplemented");
 
index 4c10d4e0ed5daa3bed980b34ad8e5997390247c8..df96a3d33715a3c59704cfcd9f4e63e13e30b4ba 100644 (file)
@@ -95,44 +95,44 @@ int posix_memalign(void**, size_t, size_t);
 
 version (CRuntime_Glibc)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (FreeBSD)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (NetBSD)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (OpenBSD)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (DragonFlyBSD)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (Solaris)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (Darwin)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (CRuntime_Bionic)
 {
     // Added since Lollipop
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (CRuntime_Musl)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 else version (CRuntime_UClibc)
 {
-    int posix_memalign(void**, size_t, size_t);
+    int posix_memalign(scope void**, size_t, size_t) pure;
 }
 
 //
index ec229dd3b2b2f11469756ec0c1d00eacba277136..3e515c4c68e35485dc24268a7745f4a6f1ec24b7 100644 (file)
@@ -1140,6 +1140,18 @@ else version (CRuntime_UClibc)
         enum __SIZEOF_PTHREAD_BARRIER_T     = 20;
         enum __SIZEOF_PTHREAD_BARRIERATTR_T = 4;
      }
+     else version (MIPS64)
+     {
+        enum __SIZEOF_PTHREAD_ATTR_T        = 56;
+        enum __SIZEOF_PTHREAD_MUTEX_T       = 40;
+        enum __SIZEOF_PTHREAD_MUTEXATTR_T   = 4;
+        enum __SIZEOF_PTHREAD_COND_T        = 48;
+        enum __SIZEOF_PTHREAD_CONDATTR_T    = 4;
+        enum __SIZEOF_PTHREAD_RWLOCK_T      = 56;
+        enum __SIZEOF_PTHREAD_RWLOCKATTR_T  = 8;
+        enum __SIZEOF_PTHREAD_BARRIER_T     = 32;
+        enum __SIZEOF_PTHREAD_BARRIERATTR_T = 4;
+     }
      else version (ARM)
      {
         enum __SIZEOF_PTHREAD_ATTR_T = 36;
index 2922e54ebae3b0edb6470a398faac65414c1ac45..798208597b145303a13be932150941c9c88dad09 100644 (file)
@@ -288,7 +288,8 @@ private:
         auto res = formatStackFrame(pc);
         res ~= " in ";
         const(char)[] tempSymName = symName[0 .. strlen(symName)];
-        //Deal with dmd mangling of long names
+        // Deal with dmd mangling of long names for OMF 32 bits builds
+        // Note that `target.d` only defines `CRuntime_DigitalMars` for OMF builds
         version (CRuntime_DigitalMars)
         {
             size_t decodeIndex = 0;
index 9b8391e720741a5cba113f27b47208fa8c112211..ea163a0958aea573cc985f78b60172feffb485f9 100644 (file)
@@ -3362,7 +3362,7 @@ struct TickDuration
         $(D gettimeofday) (the decision is made when $(D TickDuration) is
         compiled), which unfortunately, is not monotonic, but if
         $(D mach_absolute_time) and $(D clock_gettime) aren't available, then
-        $(D gettimeofday) is the the best that there is.
+        $(D gettimeofday) is the best that there is.
 
         $(RED Warning):
             On some systems, the monotonic clock may stop counting when
index d842499b8db34f85d3732bb486a043cf5310996b..83351f26038c913ee307afa4b34a71fdaff99d0a 100644 (file)
@@ -2700,7 +2700,7 @@ class Exception : Throwable
      * Creates a new instance of Exception. The nextInChain parameter is used
      * internally and should always be $(D null) when passed by user code.
      * This constructor does not automatically throw the newly-created
-     * Exception; the $(D throw) statement should be used for that purpose.
+     * Exception; the $(D throw) expression should be used for that purpose.
      */
     @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
     {
@@ -4617,6 +4617,7 @@ 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;
 public import core.internal.array.arrayassign : _d_arrayassign_r;
+public import core.internal.array.arrayassign : _d_arraysetassign;
 public import core.internal.array.capacity: _d_arraysetlengthTImpl;
 
 public import core.internal.dassert: _d_assert_fail;
diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d
deleted file mode 100644 (file)
index c9e2b15..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Implementation of array assignment support routines.
- *
- *
- * Copyright: Copyright Digital Mars 2010 - 2016.
- * License:   Distributed under the
- *            $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * Authors:   Walter Bright, Kenji Hara
- * Source:    $(DRUNTIMESRC rt/_arrayassign.d)
- */
-
-module rt.arrayassign;
-
-private
-{
-    import core.internal.util.array;
-    import core.stdc.string;
-    import core.stdc.stdlib;
-    debug(PRINTF) import core.stdc.stdio;
-}
-
-/**
-Set all elements of an array to a single value.
-
----
-p[0 .. count] = value;
----
-
-Takes into account postblits and destructors, for Plain Old Data elements,
-`rt/memset.d` is used.
-
-Params:
-    p = pointer to start of array
-    value = bytes of the element to set. Size is derived from `ti`.
-    count = amount of array elements to set
-    ti = type info of the array element type / `value`
-Returns: `p`
-*/
-extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti)
-{
-    void* pstart = p;
-
-    auto element_size = ti.tsize;
-
-    // Need a temporary buffer tmp[] big enough to hold one element
-    immutable maxAllocaSize = 512;
-    void *ptmp = (element_size > maxAllocaSize) ? malloc(element_size) : alloca(element_size);
-
-    foreach (i; 0 .. count)
-    {
-        memcpy(ptmp, p, element_size);
-        memcpy(p, value, element_size);
-        ti.postblit(p);
-        ti.destroy(ptmp);
-        p += element_size;
-    }
-    if (element_size > maxAllocaSize)
-        free(ptmp);
-    return pstart;
-}
index 026001f5ad7082e51fc879f63873b4af185d2766..f2515c3c6776264f893ef71e6aa0e91994bad061 100644 (file)
@@ -1229,61 +1229,6 @@ debug(PRINTF)
     }
 }
 
-/**
- *
- */
-extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) @weak
-{
-    if (p)
-    {
-        auto bic = __getBlkInfo(p.ptr);
-        auto info = bic ? *bic : GC.query(p.ptr);
-
-        if (info.base && (info.attr & BlkAttr.APPENDABLE))
-        {
-            if (ti) // ti non-null only if ti is a struct with dtor
-            {
-                void* start = __arrayStart(info);
-                size_t length = __arrayAllocLength(info, ti);
-                finalize_array(start, length, ti);
-            }
-
-            // if p is in the cache, clear it there as well
-            if (bic)
-                bic.base = null;
-
-            GC.free(info.base);
-            *p = null;
-        }
-    }
-}
-
-deprecated unittest
-{
-    __gshared size_t countDtor = 0;
-    struct S
-    {
-        int x;
-        ~this() { countDtor++; }
-    }
-    // destroy large array with x.ptr not base address of allocation
-    auto x = new S[10000];
-    void* p = x.ptr;
-    assert(GC.addrOf(p) != null);
-    _d_delarray_t(cast(void[]*)&x, typeid(typeof(x[0]))); // delete x;
-    assert(GC.addrOf(p) == null);
-    assert(countDtor == 10000);
-
-    // destroy full array even if only slice passed
-    auto y = new S[400];
-    auto z = y[200 .. 300];
-    p = z.ptr;
-    assert(GC.addrOf(p) != null);
-    _d_delarray_t(cast(void[]*)&z, typeid(typeof(z[0]))); // delete z;
-    assert(GC.addrOf(p) == null);
-    assert(countDtor == 10000 + 400);
-}
-
 /**
  *
  */
@@ -2754,11 +2699,6 @@ deprecated unittest
         }
     }
 
-    dtorCount = 0;
-    S1[] arr1 = new S1[7];
-    _d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1;
-    assert(dtorCount == 7);
-
     dtorCount = 0;
     S1* s2 = new S1;
     GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
index c8a377143033379fb1250322f9118eb3a0878418..4d6382617dfc54b1da2c0d2fb9344bcf94be58e8 100644 (file)
@@ -1,4 +1,4 @@
-b578dfad94770574d7e522557a77276c35943daa
+88aa69b14f8a28255a0ac7626f6509a13cfdb67a
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
index 2413024e162486159a6762dc80de3a07850da95b..655c02eb5d4cbfbcb7f265b80e468351d4c5031c 100644 (file)
@@ -97,9 +97,9 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
        std/datetime/interval.d std/datetime/package.d \
        std/datetime/stopwatch.d std/datetime/systime.d \
        std/datetime/timezone.d std/demangle.d std/digest/crc.d \
-       std/digest/digest.d std/digest/hmac.d std/digest/md.d \
-       std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d \
-       std/digest/sha.d std/encoding.d std/exception.d \
+       std/digest/hmac.d std/digest/md.d std/digest/murmurhash.d \
+       std/digest/package.d std/digest/ripemd.d std/digest/sha.d \
+       std/encoding.d std/exception.d \
        std/experimental/allocator/building_blocks/affix_allocator.d \
        std/experimental/allocator/building_blocks/aligned_block_list.d \
        std/experimental/allocator/building_blocks/allocator_list.d \
@@ -156,6 +156,6 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
        std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d \
        std/typecons.d std/typetuple.d std/uni/package.d std/uri.d std/utf.d \
        std/uuid.d std/variant.d std/windows/charset.d std/windows/registry.d \
-       std/windows/syserror.d std/xml.d std/zip.d std/zlib.d
+       std/windows/syserror.d std/zip.d std/zlib.d
 
 endif
index 562a428412f06fdea9741688f54e09df0e1fe36b..a6229587e7b370f81e6f337809332d6c6fca7b59 100644 (file)
@@ -179,7 +179,6 @@ am__dirstamp = $(am__leading_dot)dirstamp
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/datetime/timezone.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/demangle.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/crc.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/digest.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/hmac.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/md.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/murmurhash.lo \
@@ -291,8 +290,7 @@ am__dirstamp = $(am__leading_dot)dirstamp
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/windows/charset.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/windows/registry.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/windows/syserror.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/xml.lo std/zip.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/zlib.lo
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/zip.lo std/zlib.lo
 am__objects_2 = $(am__objects_1)
 am__objects_3 = $(am__objects_2)
 am_libgphobos_la_OBJECTS = $(am__objects_3)
@@ -562,9 +560,9 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/datetime/interval.d std/datetime/package.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/datetime/stopwatch.d std/datetime/systime.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/datetime/timezone.d std/demangle.d std/digest/crc.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/digest.d std/digest/hmac.d std/digest/md.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/sha.d std/encoding.d std/exception.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/hmac.d std/digest/md.d std/digest/murmurhash.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/digest/package.d std/digest/ripemd.d std/digest/sha.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/encoding.d std/exception.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/experimental/allocator/building_blocks/affix_allocator.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/experimental/allocator/building_blocks/aligned_block_list.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/experimental/allocator/building_blocks/allocator_list.d \
@@ -621,7 +619,7 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/typecons.d std/typetuple.d std/uni/package.d std/uri.d std/utf.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/uuid.d std/variant.d std/windows/charset.d std/windows/registry.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/windows/syserror.d std/xml.d std/zip.d std/zlib.d
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/windows/syserror.d std/zip.d std/zlib.d
 
 
 # Source file definitions. Boring stuff, auto-generated with
@@ -754,7 +752,6 @@ std/digest/$(am__dirstamp):
        @$(MKDIR_P) std/digest
        @: > std/digest/$(am__dirstamp)
 std/digest/crc.lo: std/digest/$(am__dirstamp)
-std/digest/digest.lo: std/digest/$(am__dirstamp)
 std/digest/hmac.lo: std/digest/$(am__dirstamp)
 std/digest/md.lo: std/digest/$(am__dirstamp)
 std/digest/murmurhash.lo: std/digest/$(am__dirstamp)
@@ -964,7 +961,6 @@ std/windows/$(am__dirstamp):
 std/windows/charset.lo: std/windows/$(am__dirstamp)
 std/windows/registry.lo: std/windows/$(am__dirstamp)
 std/windows/syserror.lo: std/windows/$(am__dirstamp)
-std/xml.lo: std/$(am__dirstamp)
 std/zip.lo: std/$(am__dirstamp)
 std/zlib.lo: std/$(am__dirstamp)
 
index 45c248e117866643294f6c9d4b16a691f7ec5059..481222596702c791059fb90b3bdbdbc6793d06f0 100644 (file)
@@ -70,10 +70,6 @@ $(BOOKTABLE ,
         $(TDNW $(MREF std,json))
         $(TD Read/write data in JSON format.)
     )
-    $(TR
-        $(TDNW $(MREF std,xml))
-        $(TD Read/write data in XML format.)
-    )
     $(TR
         $(TDNW $(MREF std,zip))
         $(TD Read/write data in the ZIP archive format.)
index b810fbb92582c55600c55bb2d6f713d5a45ce277..5ecb4f601a51e9573597da3c440f804deb43a35d 100644 (file)
@@ -577,14 +577,24 @@ 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) : T1))
+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
 {
     assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
 }
 do
 {
-    return val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val;
+    if (val.lessThan(lower))
+        return lower;
+    else if (val.greaterThan(upper))
+        return upper;
+    return val;
 }
 
 ///
@@ -637,6 +647,12 @@ do
     assert(x.clamp(lo, hi).y == 42);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=23268
+@safe pure nothrow @nogc unittest
+{
+    static assert(__traits(compiles, clamp(short.init, short.init, cast(const) short.init)));
+}
+
 // cmp
 /**********************************
 Performs a lexicographical comparison on two
index 3e828cee9bbb8d7b4b72422e6493591f7d0d52bd..39eff0d4a2b82a76e42f2acfb6745debcb1b071b 100644 (file)
@@ -771,6 +771,23 @@ private struct MapResult(alias fun, Range)
     assert(dd.length == 4);
 }
 
+// Verify fix for: https://issues.dlang.org/show_bug.cgi?id=16034
+@safe unittest
+{
+    struct One
+    {
+        int entry = 1;
+        @disable this(this);
+    }
+
+    One[] ones = [One(), One()];
+
+    import std.algorithm.comparison : equal;
+
+    assert(ones.map!`a.entry + 1`.equal([2, 2]));
+}
+
+
 @safe unittest
 {
     import std.algorithm.comparison : equal;
index 870b1b4021991a0140cc81185eb56d4b1589f97f..15f7ca99e66ead2ae8cb5b8234c073075eadfbb6 100644 (file)
@@ -5002,7 +5002,7 @@ If set to `OpenRight.yes`, then the interval is open to the right
 (last element is not included).
 
 Otherwise if set to `OpenRight.no`, then the interval is closed to the right
-(last element included).
+including the entire sentinel.
  */
 alias OpenRight = Flag!"openRight";
 
@@ -5052,6 +5052,7 @@ if (isInputRange!Range)
     static if (!is(Sentinel == void))
         private Sentinel _sentinel;
     private OpenRight _openRight;
+    private bool _matchStarted;
     private bool _done;
 
     static if (!is(Sentinel == void))
@@ -5063,7 +5064,19 @@ if (isInputRange!Range)
             _input = input;
             _sentinel = sentinel;
             _openRight = openRight;
-            _done = _input.empty || openRight && predSatisfied();
+            static if (isInputRange!Sentinel)
+            {
+                _matchStarted = predSatisfied();
+                _done = _input.empty || _sentinel.empty || openRight && _matchStarted;
+                if (_matchStarted && !_done && !openRight)
+                {
+                    _sentinel.popFront;
+                }
+            }
+            else
+            {
+                _done = _input.empty || openRight && predSatisfied();
+            }
         }
         private this(Range input, Sentinel sentinel, OpenRight openRight,
             bool done)
@@ -5118,9 +5131,32 @@ if (isInputRange!Range)
         assert(!empty, "Can not popFront of an empty Until");
         if (!_openRight)
         {
-            _done = predSatisfied();
-            _input.popFront();
-            _done = _done || _input.empty;
+            static if (isInputRange!Sentinel)
+            {
+                _input.popFront();
+                _done = _input.empty || _sentinel.empty;
+                if (!_done)
+                {
+                    if (_matchStarted)
+                    {
+                        _sentinel.popFront;
+                    }
+                    else
+                    {
+                        _matchStarted = predSatisfied();
+                        if (_matchStarted)
+                        {
+                            _sentinel.popFront;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                _done = predSatisfied();
+                _input.popFront();
+                _done = _done || _input.empty;
+            }
         }
         else
         {
@@ -5212,3 +5248,33 @@ pure @safe unittest
         assert(equal(r.save, "foo"));
     }
 }
+// https://issues.dlang.org/show_bug.cgi?id=14543
+pure @safe unittest
+{
+    import std.algorithm.comparison : equal;
+    import std.uni : toUpper;
+    assert("one two three".until("two").equal("one "));
+    assert("one two three".until("two", OpenRight.no).equal("one two"));
+
+    assert("one two three".until("two", No.openRight).equal("one two"));
+    assert("one two three".until("two", Yes.openRight).equal("one "));
+
+    assert("one two three".until('t', Yes.openRight).equal("one "));
+    assert("one two three".until("", Yes.openRight).equal(""));
+    assert("one two three".until("", No.openRight).equal(""));
+
+    assert("one two three".until("three", No.openRight).equal("one two three"));
+    assert("one two three".until("three", Yes.openRight).equal("one two "));
+
+    assert("one two three".until("one", No.openRight).equal("one"));
+    assert("one two three".until("one", Yes.openRight).equal(""));
+
+    assert("one two three".until("o", No.openRight).equal("o"));
+    assert("one two three".until("o", Yes.openRight).equal(""));
+
+    assert("one two three".until("", No.openRight).equal(""));
+    assert("one two three".until("", Yes.openRight).equal(""));
+
+    assert("one two three".until!((a,b)=>a.toUpper == b)("TWO", No.openRight).equal("one two"));
+}
+
index ee68b234b1510a426da714520559e85046733740..4fc7ee9ac4ca7926b0c95159f1be332d06520d6d 100644 (file)
@@ -1642,9 +1642,9 @@ private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
 }
 
 // https://issues.dlang.org/show_bug.cgi?id=16413 - @system comparison function
-@safe unittest
+@system unittest
 {
-    bool lt(int a, int b) { return a < b; } static @system
+    static @system bool lt(int a, int b) { return a < b; }
     auto a = [2, 1];
     a.multiSort!(lt, lt);
     assert(a == [1, 2]);
index 2f983c58fe088cc4f4bcaebc13ae6f12365130cb..4ea5bd72433731a7f0356c2a9139f1c7d00852d9 100644 (file)
@@ -28,7 +28,7 @@ immutable
         gnu = 2,         /// GNU D Compiler (GDC)
         llvm = 3,        /// LLVM D Compiler (LDC)
         dotNET = 4,      /// D.NET
-        sdc = 5,         /// Stupid D Compiler (SDC)
+        sdc = 5,         /// Snazzy D Compiler (SDC)
     }
 
     /// Which vendor produced this compiler.
index fb383ae3f2e239d654d181828101d2011b245599..bc53236b9b71be818bf01de6960b2ed3eac9b423 100644 (file)
@@ -257,9 +257,12 @@ private
 
     @property ref ThreadInfo thisInfo() nothrow
     {
-        if (scheduler is null)
+        import core.atomic : atomicLoad;
+
+        auto localScheduler = atomicLoad(scheduler);
+        if (localScheduler is null)
             return ThreadInfo.thisInfo;
-        return scheduler.thisInfo;
+        return localScheduler.thisInfo;
     }
 }
 
index 08f9ead196ee2d804a3b02633f83f5c57129604d..ecc459969254b7b2089c410f290888241a41d739 100644 (file)
@@ -412,9 +412,9 @@ if (!is(immutable T == immutable bool))
                     .destroy(e);
 
             static if (hasIndirections!T)
-                GC.removeRange(_payload.ptr);
+                GC.removeRange(cast(void*) _payload.ptr);
 
-            free(_payload.ptr);
+            free(cast(void*) _payload.ptr);
         }
 
         this(this) @disable;
@@ -489,14 +489,14 @@ if (!is(immutable T == immutable bool))
                 auto newPayload = newPayloadPtr[0 .. oldLength];
 
                 // copy old data over to new array
-                memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength);
+                memcpy(cast(void*) newPayload.ptr, cast(void*) _payload.ptr, T.sizeof * oldLength);
                 // Zero out unused capacity to prevent gc from seeing false pointers
-                memset(newPayload.ptr + oldLength,
+                memset( cast(void*) (newPayload.ptr + oldLength),
                         0,
                         (elements - oldLength) * T.sizeof);
-                GC.addRange(newPayload.ptr, sz);
-                GC.removeRange(_payload.ptr);
-                free(_payload.ptr);
+                GC.addRange(cast(void*) newPayload.ptr, sz);
+                GC.removeRange(cast(void*) _payload.ptr);
+                free(cast(void*) _payload.ptr);
                 _payload = newPayload;
             }
             else
@@ -611,12 +611,17 @@ if (!is(immutable T == immutable bool))
         return opEquals(rhs);
     }
 
+    // fix https://issues.dlang.org/show_bug.cgi?23140
+    private alias Unshared(T) = T;
+    private alias Unshared(T: shared U, U) = U;
+
     /// ditto
     bool opEquals(ref const Array rhs) const
     {
         if (empty) return rhs.empty;
         if (rhs.empty) return false;
-        return _data._payload == rhs._data._payload;
+
+        return cast(Unshared!(T)[]) _data._payload ==  cast(Unshared!(T)[]) rhs._data._payload;
     }
 
     /**
@@ -1740,6 +1745,16 @@ if (!is(immutable T == immutable bool))
     assertThrown!AssertError(array.length = 5);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=23140
+@system unittest
+{
+    shared class C
+    {
+    }
+
+    Array!C ac;
+    ac = Array!C([new C]);
+}
 ////////////////////////////////////////////////////////////////////////////////
 // Array!bool
 ////////////////////////////////////////////////////////////////////////////////
index 32d56ecc73396b972d4292e5a05376cfa744ae51..11e3883e5ee408203f3fccf00a06dea021f386d8 100644 (file)
@@ -111,7 +111,7 @@ private struct DRange
         static assert(is(ElementType!DRange == BaseNode*));
     }
 
-nothrow @safe pure:
+nothrow @safe @nogc pure:
     private BaseNode* _first;
     private BaseNode* _last;
 
index 9164e0798659bd05cef9dca5b080920be963b4ae..0f660658920273d7ce05dd3370b9ef3ffa3bb38f 100644 (file)
@@ -4894,7 +4894,7 @@ if (isOctalLiteral(num))
 template octal(alias decimalInteger)
 if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
 {
-    enum octal = octal!(typeof(decimalInteger))(to!string(decimalInteger));
+    enum octal = convertToOctal(decimalInteger);
 }
 
 ///
@@ -4910,6 +4910,19 @@ if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
     auto d = octal!"0001_200_000";
 }
 
+/*************************************
+ * Convert a decimal integer to an octal integer with the same digits.
+ * Params:
+ *    i = integer to convert
+ * Returns:
+ *    octal integer with the same type and same digits
+ */
+private T convertToOctal(T)(T i)
+{
+    assert((i % 10) < 8);
+    return i ? convertToOctal(i / 10) * 8 + i % 10 : 0;
+}
+
 /*
     Takes a string, num, which is an octal literal, and returns its
     value, in the type T specified.
index d8ecf7dbfbb65e3ded8c73747167876fcb637559..eedc0ea79c90c76765d0b7083fdda5c8789993b9 100644 (file)
@@ -234,7 +234,7 @@ public:
 
 
     /++
-       Peek at the amount of time that the the StopWatch has been running.
+       Peek at the amount of time that the StopWatch has been running.
 
        This does not include any time during which the StopWatch was stopped but
        does include $(I all) of the time that it was running and not just the
index 28255f4ef993e98d1e0aa4079d55f722ea6a46fa..b23891808f5d70b319404c389c2060c815096a0a 100644 (file)
@@ -3397,8 +3397,7 @@ struct TZConversions
 TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure
 {
     // This is a bit hacky, since it doesn't properly read XML, but it avoids
-    // needing to pull in std.xml (which we're theoretically replacing at some
-    // point anyway).
+    // needing to pull in an xml parsing module.
     import std.algorithm.iteration : uniq;
     import std.algorithm.searching : find;
     import std.algorithm.sorting : sort;
index 38563b14cd0cfc8112799bb5f7df43cb57df6021..b7922bb03da84cc14621c3ced47071c543afb4d5 100644 (file)
@@ -555,7 +555,7 @@ ubyte[8] crc64ECMAOf(T...)(T data)
 }
 
 /**
- * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * This is a convenience alias for $(REF digest, std,digest) using the
  * CRC64-ISO implementation.
  *
  * Params:
@@ -611,7 +611,7 @@ alias CRC32Digest = WrapperDigest!CRC32;
  * OOP API CRC64-ECMA implementation.
  * See `std.digest` for differences between template and OOP API.
  *
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA),
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC64ECMA),
  * see there for more information.
  */
 alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
@@ -620,7 +620,7 @@ alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
  * OOP API CRC64-ISO implementation.
  * See `std.digest` for differences between template and OOP API.
  *
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO),
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC64ISO),
  * see there for more information.
  */
 alias CRC64ISODigest = WrapperDigest!CRC64ISO;
@@ -628,7 +628,7 @@ alias CRC64ISODigest = WrapperDigest!CRC64ISO;
 ///
 @safe unittest
 {
-    //Simple example, hashing a string using Digest.digest helper function
+    //Simple example, hashing a string using CRC32Digest.digest helper function
     auto crc = new CRC32Digest();
     ubyte[] hash = crc.digest("abc");
     //Let's get a hash string
diff --git a/libphobos/src/std/digest/digest.d b/libphobos/src/std/digest/digest.d
deleted file mode 100644 (file)
index 01fdbd7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-// @@@DEPRECATED_2.101@@@
-deprecated("import std.digest instead of std.digest.digest. std.digest.digest will be removed in 2.101")
-module std.digest.digest;
index 1e3df91f95ed70823ea518964633014af0fd9f3b..d0d0b7cb27cf744bef188ae30c02d2448e6a2f72 100644 (file)
@@ -521,9 +521,9 @@ version (StdUnittest)
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
 
-    auto a = AffixAllocator!(Region!(), uint)(Region!()(new ubyte[1024 * 64]));
+    auto a = AffixAllocator!(BorrowedRegion!(), uint)(BorrowedRegion!()(new ubyte[1024 * 64]));
     auto b = a.allocate(42);
     assert(b.length == 42);
     // Test that expand infers from parent
index 14c6de4696f65c9c00d351ba4d91fcf2bc997774..99768bc318e56d3ac564dc7e7dc4193d80886652 100644 (file)
@@ -526,7 +526,7 @@ shared struct SharedAlignedBlockList(Allocator, ParentAllocator, ulong theAlignm
 ///
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : SharedRegion;
+    import std.experimental.allocator.building_blocks.region : SharedBorrowedRegion;
     import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
     import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
     import core.thread : ThreadGroup;
@@ -536,11 +536,11 @@ shared struct SharedAlignedBlockList(Allocator, ParentAllocator, ulong theAlignm
     enum maxIter = 10;
 
     /*
-    In this example we use 'SharedAlignedBlockList' together with 'SharedRegion',
-    in order to create a fast, thread-safe allocator.
+    In this example we use 'SharedAlignedBlockList' together with
+    'SharedBorrowedRegion', in order to create a fast, thread-safe allocator.
     */
     alias SuperAllocator = SharedAlignedBlockList!(
-            SharedRegion!(NullAllocator, 1),
+            SharedBorrowedRegion!(1),
             SharedAscendingPageAllocator,
             4096);
 
@@ -597,7 +597,7 @@ version (StdUnittest)
     SpinLock lock = SpinLock(SpinLock.Contention.brief);
 
     alias SuperAllocator = SharedAlignedBlockList!(
-            SharedRegion!(NullAllocator, 1),
+            SharedBorrowedRegion!(1),
             SharedAscendingPageAllocator,
             1 << 16);
     void[][totalAllocs] buf;
index bcab16d6d8f1a7ad5921cc2fc59e0e512df31650..ca83785914eb777b4d7155c282e1c981557a6c3d 100644 (file)
@@ -679,8 +679,8 @@ version (Posix) @system unittest
 {
     // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : Region;
-    AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
     auto b1 = a.alignedAllocate(1024 * 8192, 1024);
     assert(b1 !is null); // still works due to overdimensioning
     assert(b1.length == 1024 * 8192);
@@ -707,8 +707,8 @@ version (Posix) @system unittest
 
     // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : Region;
-    AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
     auto b1 = a.alignedAllocate(0, 1);
     assert(b1 is null);
 
@@ -728,8 +728,8 @@ version (Posix) @system unittest
 
     // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : Region;
-    AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
     auto b0 = a.alignedAllocate(1, 1024);
     assert(b0.length == 1);
     assert(b0.ptr.alignedAt(1024));
@@ -765,8 +765,8 @@ version (Posix) @system unittest
 {
     // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : Region;
-    AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
     auto b1 = a.allocate(1024 * 8192);
     assert(b1 !is null); // still works due to overdimensioning
     b1 = a.allocate(1024 * 10);
@@ -779,10 +779,10 @@ version (Posix) @system unittest
 @system unittest
 {
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
     import std.experimental.allocator.mallocator : Mallocator;
     import std.typecons : Ternary;
-    AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a;
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a;
     auto b1 = a.allocate(1024 * 8192);
     assert(b1 !is null);
     b1 = a.allocate(1024 * 10);
index b413d738dab0350b98560281481e99b188446763..3990418c3cfb4aa9350dd9c19a40803cfae6e3f4 100644 (file)
@@ -342,12 +342,12 @@ struct FallbackAllocator(Primary, Fallback)
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
     import std.typecons : Ternary;
 
-    auto a = FallbackAllocator!(Region!(), Region!())(
-                Region!()(new ubyte[4096 * 1024]),
-                Region!()(new ubyte[4096 * 1024]));
+    auto a = FallbackAllocator!(BorrowedRegion!(), BorrowedRegion!())(
+                BorrowedRegion!()(new ubyte[4096 * 1024]),
+                BorrowedRegion!()(new ubyte[4096 * 1024]));
 
     auto b = a.alignedAllocate(42, 8);
     assert(b.length == 42);
@@ -506,11 +506,11 @@ version (StdUnittest)
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
     import std.typecons : Ternary;
 
-    alias A = FallbackAllocator!(Region!(), Region!());
-    auto a = A(Region!()(new ubyte[16_384]), Region!()(new ubyte[16_384]));
+    alias A = FallbackAllocator!(BorrowedRegion!(), BorrowedRegion!());
+    auto a = A(BorrowedRegion!()(new ubyte[16_384]), BorrowedRegion!()(new ubyte[16_384]));
 
     auto b = a.allocate(42);
     assert(b.length == 42);
index 7055d666fb11ee95a1445826717dcde55df28b95..d2b32099226b5dfda657949efb6c5828b2283074 100644 (file)
@@ -486,9 +486,9 @@ struct FreeList(ParentAllocator,
 // Test that deallocateAll infers from parent
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
 
-    auto fl = FreeList!(Region!(), 0, 16)(Region!()(new ubyte[1024 * 64]));
+    auto fl = FreeList!(BorrowedRegion!(), 0, 16)(BorrowedRegion!()(new ubyte[1024 * 64]));
     auto b = fl.allocate(42);
     assert(b.length == 42);
     assert((() pure nothrow @safe @nogc => fl.expand(b, 48))());
index bd4bb9511ea14afc233214acfeed81705628cbfd..fe59e2650a168f509233a19dad57f302cf2aca92 100644 (file)
@@ -502,9 +502,9 @@ version (StdUnittest)
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
 
-    auto a = FreeTree!(Region!())(Region!()(new ubyte[1024 * 64]));
+    auto a = FreeTree!(BorrowedRegion!())(BorrowedRegion!()(new ubyte[1024 * 64]));
     auto b = a.allocate(42);
     assert(b.length == 42);
     assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
index 762b3797b41f7ff2a5a38fa9a1676de0db416671..3334a86fd849c9b51ee44a32ae2c4e4d04090b22 100644 (file)
@@ -315,14 +315,14 @@ version (StdUnittest)
 version (StdUnittest)
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
     import std.typecons : Ternary;
 
-    alias MyAlloc = Quantizer!(Region!(),
+    alias MyAlloc = Quantizer!(BorrowedRegion!(),
         (size_t n) => n.roundUpToMultipleOf(64));
-    testAllocator!(() => MyAlloc(Region!()(new ubyte[1024 * 64])));
+    testAllocator!(() => MyAlloc(BorrowedRegion!()(new ubyte[1024 * 64])));
 
-    auto a = MyAlloc(Region!()(new ubyte[1024 * 64]));
+    auto a = MyAlloc(BorrowedRegion!()(new ubyte[1024 * 64]));
     // Check that empty inherits from parent
     assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
     auto b = a.allocate(42);
index be0d274633c65b48142eefd35a57a570773766d6..8c397849f4327a74aef4335d5634e7270bb95024 100644 (file)
@@ -29,8 +29,7 @@ the store and the limits. One allocation entails rounding up the allocation
 size for alignment purposes, bumping the current pointer, and comparing it
 against the limit.
 
-If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), `Region`
-deallocates the chunk of memory during destruction.
+`Region` deallocates the chunk of memory during destruction.
 
 The `minAlign` parameter establishes alignment. If $(D minAlign > 1), the
 sizes of all allocation requests are rounded up to a multiple of `minAlign`.
@@ -38,7 +37,7 @@ Applications aiming at maximum speed may want to choose $(D minAlign = 1) and
 control alignment externally.
 
 */
-struct Region(ParentAllocator = NullAllocator,
+struct Region(ParentAllocator,
     uint minAlign = platformAlignment,
     Flag!"growDownwards" growDownwards = No.growDownwards)
 {
@@ -63,50 +62,42 @@ struct Region(ParentAllocator = NullAllocator,
         alias parent = ParentAllocator.instance;
     }
 
-    private void* _current, _begin, _end;
+    private BorrowedRegion!(minAlign, growDownwards) _impl;
 
     private void* roundedBegin() const pure nothrow @trusted @nogc
     {
-        return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+        return _impl.roundedBegin;
     }
 
     private void* roundedEnd() const pure nothrow @trusted @nogc
     {
-        return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+        return _impl.roundedEnd;
     }
     /**
     Constructs a region backed by a user-provided store.
-    Assumes the memory was allocated with `ParentAllocator`
-    (if different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator)).
+    Assumes the memory was allocated with `ParentAllocator`.
 
     Params:
-        store = User-provided store backing up the region. If $(D
-        ParentAllocator) is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), memory is assumed to
-        have been allocated with `ParentAllocator`.
-        n = Bytes to allocate using `ParentAllocator`. This constructor is only
-        defined If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator). If
-        `parent.allocate(n)` returns `null`, the region will be initialized
-        as empty (correctly initialized but unable to allocate).
+        store = User-provided store backing up the region. Assumed to have been
+        allocated with `ParentAllocator`.
+        n = Bytes to allocate using `ParentAllocator`. If `parent.allocate(n)`
+        returns `null`, the region will be initialized as empty (correctly
+        initialized but unable to allocate).
         */
     this(ubyte[] store) pure nothrow @nogc
     {
-        _begin = store.ptr;
-        _end = store.ptr + store.length;
-        static if (growDownwards)
-            _current = roundedEnd();
-        else
-            _current = roundedBegin();
+        _impl = store;
     }
 
     /// Ditto
-    static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator)
+    static if (!stateSize!ParentAllocator)
     this(size_t n)
     {
         this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
     }
 
     /// Ditto
-    static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator)
+    static if (stateSize!ParentAllocator)
     this(ParentAllocator parent, size_t n)
     {
         this.parent = parent;
@@ -119,15 +110,13 @@ struct Region(ParentAllocator = NullAllocator,
     */
 
     /**
-    If `ParentAllocator` is not $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator) and defines `deallocate`,
-    the region defines a destructor that uses `ParentAllocator.deallocate` to free the
-    memory chunk.
+    If `ParentAllocator` defines `deallocate`, the region defines a destructor
+    that uses `ParentAllocator.deallocate` to free the memory chunk.
     */
-    static if (!is(ParentAllocator == NullAllocator)
-        && hasMember!(ParentAllocator, "deallocate"))
+    static if (hasMember!(ParentAllocator, "deallocate"))
     ~this()
     {
-        parent.deallocate(_begin[0 .. _end - _begin]);
+        with (_impl) parent.deallocate(_begin[0 .. _end - _begin]);
     }
 
     /**
@@ -135,7 +124,7 @@ struct Region(ParentAllocator = NullAllocator,
     */
     size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
     {
-        return n.roundUpToAlignment(alignment);
+        return _impl.goodAllocSize(n);
     }
 
     /**
@@ -156,24 +145,7 @@ struct Region(ParentAllocator = NullAllocator,
     */
     void[] allocate(size_t n) pure nothrow @trusted @nogc
     {
-        const rounded = goodAllocSize(n);
-        if (n == 0 || rounded < n || available < rounded) return null;
-
-        static if (growDownwards)
-        {
-            assert(available >= rounded);
-            auto result = (_current - rounded)[0 .. n];
-            assert(result.ptr >= _begin);
-            _current = result.ptr;
-            assert(owns(result) == Ternary.yes);
-        }
-        else
-        {
-            auto result = _current[0 .. n];
-            _current += rounded;
-        }
-
-        return result;
+        return _impl.allocate(n);
     }
 
     /**
@@ -188,57 +160,13 @@ struct Region(ParentAllocator = NullAllocator,
     */
     void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
     {
-        import std.math.traits : isPowerOf2;
-        assert(a.isPowerOf2);
-
-        const rounded = goodAllocSize(n);
-        if (n == 0 || rounded < n || available < rounded) return null;
-
-        static if (growDownwards)
-        {
-            auto tmpCurrent = _current - rounded;
-            auto result = tmpCurrent.alignDownTo(a);
-            if (result <= tmpCurrent && result >= _begin)
-            {
-                _current = result;
-                return cast(void[]) result[0 .. n];
-            }
-        }
-        else
-        {
-            // Just bump the pointer to the next good allocation
-            auto newCurrent = _current.alignUpTo(a);
-            if (newCurrent < _current || newCurrent > _end)
-                return null;
-
-            auto save = _current;
-            _current = newCurrent;
-            auto result = allocate(n);
-            if (result.ptr)
-            {
-                assert(result.length == n);
-                return result;
-            }
-            // Failed, rollback
-            _current = save;
-        }
-        return null;
+        return _impl.alignedAllocate(n, a);
     }
 
     /// Allocates and returns all memory available to this region.
     void[] allocateAll() pure nothrow @trusted @nogc
     {
-        static if (growDownwards)
-        {
-            auto result = _begin[0 .. available];
-            _current = _begin;
-        }
-        else
-        {
-            auto result = _current[0 .. available];
-            _current = _end;
-        }
-        return result;
+        return _impl.allocateAll;
     }
 
     /**
@@ -249,25 +177,7 @@ struct Region(ParentAllocator = NullAllocator,
     static if (growDownwards == No.growDownwards)
     bool expand(ref void[] b, size_t delta) pure nothrow @safe @nogc
     {
-        assert(owns(b) == Ternary.yes || b is null);
-        assert((() @trusted => b.ptr + b.length <= _current)() || b is null);
-        if (b is null || delta == 0) return delta == 0;
-        auto newLength = b.length + delta;
-        if ((() @trusted => _current < b.ptr + b.length + alignment)())
-        {
-            immutable currentGoodSize = this.goodAllocSize(b.length);
-            immutable newGoodSize = this.goodAllocSize(newLength);
-            immutable goodDelta = newGoodSize - currentGoodSize;
-            // This was the last allocation! Allocate some more and we're done.
-            if (goodDelta == 0
-                || (() @trusted => allocate(goodDelta).length == goodDelta)())
-            {
-                b = (() @trusted => b.ptr[0 .. newLength])();
-                assert((() @trusted => _current < b.ptr + b.length + alignment)());
-                return true;
-            }
-        }
-        return false;
+        return _impl.expand(b, delta);
     }
 
     /**
@@ -281,26 +191,7 @@ struct Region(ParentAllocator = NullAllocator,
     */
     bool deallocate(void[] b) pure nothrow @nogc
     {
-        assert(owns(b) == Ternary.yes || b.ptr is null);
-        auto rounded = goodAllocSize(b.length);
-        static if (growDownwards)
-        {
-            if (b.ptr == _current)
-            {
-                _current += rounded;
-                return true;
-            }
-        }
-        else
-        {
-            if (b.ptr + rounded == _current)
-            {
-                assert(b.ptr !is null || _current is null);
-                _current = b.ptr;
-                return true;
-            }
-        }
-        return false;
+        return _impl.deallocate(b);
     }
 
     /**
@@ -309,15 +200,7 @@ struct Region(ParentAllocator = NullAllocator,
     */
     bool deallocateAll() pure nothrow @nogc
     {
-        static if (growDownwards)
-        {
-            _current = roundedEnd();
-        }
-        else
-        {
-            _current = roundedBegin();
-        }
-        return true;
+        return _impl.deallocateAll;
     }
 
     /**
@@ -332,7 +215,7 @@ struct Region(ParentAllocator = NullAllocator,
     */
     Ternary owns(const void[] b) const pure nothrow @trusted @nogc
     {
-        return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+        return _impl.owns(b);
     }
 
     /**
@@ -341,23 +224,13 @@ struct Region(ParentAllocator = NullAllocator,
     */
     Ternary empty() const pure nothrow @safe @nogc
     {
-        static if (growDownwards)
-            return Ternary(_current == roundedEnd());
-        else
-            return Ternary(_current == roundedBegin());
+        return _impl.empty;
     }
 
     /// Nonstandard property that returns bytes available for allocation.
     size_t available() const @safe pure nothrow @nogc
     {
-        static if (growDownwards)
-        {
-            return _current - _begin;
-        }
-        else
-        {
-            return _end - _current;
-        }
+        return _impl.available;
     }
 }
 
@@ -414,17 +287,6 @@ struct Region(ParentAllocator = NullAllocator,
     testAlloc(sharedReg);
 }
 
-@system nothrow @nogc unittest
-{
-    import std.experimental.allocator.mallocator : AlignedMallocator;
-    import std.typecons : Ternary;
-
-    ubyte[] buf = cast(ubyte[]) AlignedMallocator.instance.alignedAllocate(64, 64);
-    auto reg = Region!(NullAllocator, 64, Yes.growDownwards)(buf);
-    assert(reg.alignedAllocate(10, 32).length == 10);
-    assert(!reg.available);
-}
-
 @system nothrow @nogc unittest
 {
     // test 'this(ubyte[] store)' constructed regions properly clean up
@@ -491,107 +353,428 @@ version (StdUnittest)
 }
 
 /**
+A `BorrowedRegion` allocates directly from a user-provided block of memory.
 
-`InSituRegion` is a convenient region that carries its storage within itself
-(in the form of a statically-sized array).
-
-The first template argument is the size of the region and the second is the
-needed alignment. Depending on the alignment requested and platform details,
-the actual available storage may be smaller than the compile-time parameter. To
-make sure that at least `n` bytes are available in the region, use
-$(D InSituRegion!(n + a - 1, a)).
-
-Given that the most frequent use of `InSituRegion` is as a stack allocator, it
-allocates starting at the end on systems where stack grows downwards, such that
-hot memory is used first.
+Unlike a `Region`, a `BorrowedRegion` does not own the memory it allocates from
+and will not deallocate that memory upon destruction. Instead, it is the user's
+responsibility to ensure that the memory is properly disposed of.
 
+In all other respects, a `BorrowedRegion` behaves exactly like a `Region`.
 */
-struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
+struct BorrowedRegion(uint minAlign = platformAlignment,
+    Flag!"growDownwards" growDownwards = No.growDownwards)
 {
-    import std.algorithm.comparison : max;
-    import std.conv : to;
-    import std.traits : hasMember;
-    import std.typecons : Ternary;
-
     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.");
+    import std.typecons : Ternary;
 
-    @disable this(this);
+    // state
+    private void* _current, _begin, _end;
 
-    // state {
-    private Region!(NullAllocator, minAlign, growDownwards) _impl;
-    union
+    private void* roundedBegin() const pure nothrow @trusted @nogc
     {
-        private ubyte[size] _store = void;
-        private double _forAlignmentOnly1;
+        return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+    }
+
+    private void* roundedEnd() const pure nothrow @trusted @nogc
+    {
+        return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
     }
-    // }
 
     /**
-    An alias for `minAlign`, which must be a valid alignment (nonzero power
-    of 2). The start of the region and all allocation requests will be rounded
-    up to a multiple of the alignment.
+    Constructs a region backed by a user-provided store.
 
-    ----
-    InSituRegion!(4096) a1;
-    assert(a1.alignment == platformAlignment);
-    InSituRegion!(4096, 64) a2;
-    assert(a2.alignment == 64);
-    ----
+    Params:
+        store = User-provided store backing up the region.
     */
-    alias alignment = minAlign;
-
-    private void lazyInit()
+    this(ubyte[] store) pure nothrow @nogc
     {
-        assert(!_impl._current);
-        _impl = typeof(_impl)(_store);
-        assert(_impl._current.alignedAt(alignment));
+        _begin = store.ptr;
+        _end = store.ptr + store.length;
+        static if (growDownwards)
+            _current = roundedEnd();
+        else
+            _current = roundedBegin();
     }
 
+    /*
+    TODO: The postblit of `BorrowedRegion` should be disabled because such objects
+    should not be copied around naively.
+    */
+
     /**
-    Allocates `bytes` and returns them, or `null` if the region cannot
-    accommodate the request. For efficiency reasons, if $(D bytes == 0) the
-    function returns an empty non-null slice.
+    Rounds the given size to a multiple of the `alignment`
     */
-    void[] allocate(size_t n)
+    size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
     {
-        // Fast path
-    entry:
-        auto result = _impl.allocate(n);
-        if (result.length == n) return result;
-        // Slow path
-        if (_impl._current) return null; // no more room
-        lazyInit;
-        assert(_impl._current);
-        goto entry;
+        return n.roundUpToAlignment(alignment);
     }
 
     /**
-    As above, but the memory allocated is aligned at `a` bytes.
+    Alignment offered.
     */
-    void[] alignedAllocate(size_t n, uint a)
-    {
-        // Fast path
-    entry:
-        auto result = _impl.alignedAllocate(n, a);
-        if (result.length == n) return result;
-        // Slow path
+    alias alignment = minAlign;
+
+    /**
+    Allocates `n` bytes of memory. The shortest path involves an alignment
+    adjustment (if $(D alignment > 1)), an increment, and a comparison.
+
+    Params:
+        n = number of bytes to allocate
+
+    Returns:
+        A properly-aligned buffer of size `n` or `null` if request could not
+        be satisfied.
+    */
+    void[] allocate(size_t n) pure nothrow @trusted @nogc
+    {
+        const rounded = goodAllocSize(n);
+        if (n == 0 || rounded < n || available < rounded) return null;
+
+        static if (growDownwards)
+        {
+            assert(available >= rounded);
+            auto result = (_current - rounded)[0 .. n];
+            assert(result.ptr >= _begin);
+            _current = result.ptr;
+            assert(owns(result) == Ternary.yes);
+        }
+        else
+        {
+            auto result = _current[0 .. n];
+            _current += rounded;
+        }
+
+        return result;
+    }
+
+    /**
+    Allocates `n` bytes of memory aligned at alignment `a`.
+
+    Params:
+        n = number of bytes to allocate
+        a = alignment for the allocated block
+
+    Returns:
+        Either a suitable block of `n` bytes aligned at `a`, or `null`.
+    */
+    void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
+    {
+        import std.math.traits : isPowerOf2;
+        assert(a.isPowerOf2);
+
+        const rounded = goodAllocSize(n);
+        if (n == 0 || rounded < n || available < rounded) return null;
+
+        static if (growDownwards)
+        {
+            auto tmpCurrent = _current - rounded;
+            auto result = tmpCurrent.alignDownTo(a);
+            if (result <= tmpCurrent && result >= _begin)
+            {
+                _current = result;
+                return cast(void[]) result[0 .. n];
+            }
+        }
+        else
+        {
+            // Just bump the pointer to the next good allocation
+            auto newCurrent = _current.alignUpTo(a);
+            if (newCurrent < _current || newCurrent > _end)
+                return null;
+
+            auto save = _current;
+            _current = newCurrent;
+            auto result = allocate(n);
+            if (result.ptr)
+            {
+                assert(result.length == n);
+                return result;
+            }
+            // Failed, rollback
+            _current = save;
+        }
+        return null;
+    }
+
+    /// Allocates and returns all memory available to this region.
+    void[] allocateAll() pure nothrow @trusted @nogc
+    {
+        static if (growDownwards)
+        {
+            auto result = _begin[0 .. available];
+            _current = _begin;
+        }
+        else
+        {
+            auto result = _current[0 .. available];
+            _current = _end;
+        }
+        return result;
+    }
+
+    /**
+    Expands an allocated block in place. Expansion will succeed only if the
+    block is the last allocated. Defined only if `growDownwards` is
+    `No.growDownwards`.
+    */
+    static if (growDownwards == No.growDownwards)
+    bool expand(ref void[] b, size_t delta) pure nothrow @safe @nogc
+    {
+        assert(owns(b) == Ternary.yes || b is null);
+        assert((() @trusted => b.ptr + b.length <= _current)() || b is null);
+        if (b is null || delta == 0) return delta == 0;
+        auto newLength = b.length + delta;
+        if ((() @trusted => _current < b.ptr + b.length + alignment)())
+        {
+            immutable currentGoodSize = this.goodAllocSize(b.length);
+            immutable newGoodSize = this.goodAllocSize(newLength);
+            immutable goodDelta = newGoodSize - currentGoodSize;
+            // This was the last allocation! Allocate some more and we're done.
+            if (goodDelta == 0
+                || (() @trusted => allocate(goodDelta).length == goodDelta)())
+            {
+                b = (() @trusted => b.ptr[0 .. newLength])();
+                assert((() @trusted => _current < b.ptr + b.length + alignment)());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+    Deallocates `b`. This works only if `b` was obtained as the last call
+    to `allocate`; otherwise (i.e. another allocation has occurred since) it
+    does nothing.
+
+    Params:
+        b = Block previously obtained by a call to `allocate` against this
+        allocator (`null` is allowed).
+    */
+    bool deallocate(void[] b) pure nothrow @nogc
+    {
+        assert(owns(b) == Ternary.yes || b.ptr is null);
+        auto rounded = goodAllocSize(b.length);
+        static if (growDownwards)
+        {
+            if (b.ptr == _current)
+            {
+                _current += rounded;
+                return true;
+            }
+        }
+        else
+        {
+            if (b.ptr + rounded == _current)
+            {
+                assert(b.ptr !is null || _current is null);
+                _current = b.ptr;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+    Deallocates all memory allocated by this region, which can be subsequently
+    reused for new allocations.
+    */
+    bool deallocateAll() pure nothrow @nogc
+    {
+        static if (growDownwards)
+        {
+            _current = roundedEnd();
+        }
+        else
+        {
+            _current = roundedBegin();
+        }
+        return true;
+    }
+
+    /**
+    Queries whether `b` has been allocated with this region.
+
+    Params:
+        b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns
+        `false`).
+
+    Returns:
+        `true` if `b` has been allocated with this region, `false` otherwise.
+    */
+    Ternary owns(const void[] b) const pure nothrow @trusted @nogc
+    {
+        return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+    }
+
+    /**
+    Returns `Ternary.yes` if no memory has been allocated in this region,
+    `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
+    */
+    Ternary empty() const pure nothrow @safe @nogc
+    {
+        static if (growDownwards)
+            return Ternary(_current == roundedEnd());
+        else
+            return Ternary(_current == roundedBegin());
+    }
+
+    /// Nonstandard property that returns bytes available for allocation.
+    size_t available() const @safe pure nothrow @nogc
+    {
+        static if (growDownwards)
+        {
+            return _current - _begin;
+        }
+        else
+        {
+            return _end - _current;
+        }
+    }
+}
+
+///
+@system nothrow @nogc unittest
+{
+    import std.typecons : Ternary;
+
+    ubyte[1024] store;
+    auto myRegion = BorrowedRegion!(1)(store[]);
+
+    assert(myRegion.empty == Ternary.yes);
+    assert(myRegion.available == store.length);
+
+    void[] b = myRegion.allocate(101);
+
+    assert(b.length == 101);
+    assert(myRegion.empty == Ternary.no);
+    assert(myRegion.owns(b) == Ternary.yes);
+    assert(myRegion.available == store.length - b.length);
+
+    void[] b2 = myRegion.allocate(256);
+
+    // Can only free the most recent allocation
+    assert(myRegion.deallocate(b) == false);
+    assert(myRegion.deallocate(b2) == true);
+
+    myRegion.deallocateAll();
+
+    assert(myRegion.empty == Ternary.yes);
+}
+
+@system nothrow @nogc unittest
+{
+    import std.experimental.allocator.mallocator : AlignedMallocator;
+    import std.typecons : Ternary;
+
+    ubyte[] buf = cast(ubyte[]) AlignedMallocator.instance.alignedAllocate(64, 64);
+    auto reg = BorrowedRegion!(64, Yes.growDownwards)(buf);
+    assert(reg.alignedAllocate(10, 32).length == 10);
+    assert(!reg.available);
+}
+
+/**
+
+`InSituRegion` is a convenient region that carries its storage within itself
+(in the form of a statically-sized array).
+
+The first template argument is the size of the region and the second is the
+needed alignment. Depending on the alignment requested and platform details,
+the actual available storage may be smaller than the compile-time parameter. To
+make sure that at least `n` bytes are available in the region, use
+$(D InSituRegion!(n + a - 1, a)).
+
+Given that the most frequent use of `InSituRegion` is as a stack allocator, it
+allocates starting at the end on systems where stack grows downwards, such that
+hot memory is used first.
+
+*/
+struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
+{
+    import std.algorithm.comparison : max;
+    import std.conv : to;
+    import std.traits : hasMember;
+    import std.typecons : Ternary;
+
+    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.");
+
+    @disable this(this);
+
+    // state {
+    private BorrowedRegion!(minAlign, growDownwards) _impl;
+    union
+    {
+        private ubyte[size] _store = void;
+        private double _forAlignmentOnly1;
+    }
+    // }
+
+    /**
+    An alias for `minAlign`, which must be a valid alignment (nonzero power
+    of 2). The start of the region and all allocation requests will be rounded
+    up to a multiple of the alignment.
+
+    ----
+    InSituRegion!(4096) a1;
+    assert(a1.alignment == platformAlignment);
+    InSituRegion!(4096, 64) a2;
+    assert(a2.alignment == 64);
+    ----
+    */
+    alias alignment = minAlign;
+
+    private void lazyInit()
+    {
+        assert(!_impl._current);
+        _impl = typeof(_impl)(_store);
+        assert(_impl._current.alignedAt(alignment));
+    }
+
+    /**
+    Allocates `bytes` and returns them, or `null` if the region cannot
+    accommodate the request. For efficiency reasons, if $(D bytes == 0) the
+    function returns an empty non-null slice.
+    */
+    void[] allocate(size_t n)
+    {
+        // Fast path
+    entry:
+        auto result = _impl.allocate(n);
+        if (result.length == n) return result;
+        // Slow path
+        if (_impl._current) return null; // no more room
+        lazyInit;
+        assert(_impl._current);
+        goto entry;
+    }
+
+    /**
+    As above, but the memory allocated is aligned at `a` bytes.
+    */
+    void[] alignedAllocate(size_t n, uint a)
+    {
+        // Fast path
+    entry:
+        auto result = _impl.alignedAllocate(n, a);
+        if (result.length == n) return result;
+        // Slow path
         if (_impl._current) return null; // no more room
         lazyInit;
         assert(_impl._current);
@@ -992,7 +1175,7 @@ version (Posix) @system nothrow @nogc unittest
 The threadsafe version of the `Region` allocator.
 Allocations and deallocations are lock-free based using $(REF cas, core,atomic).
 */
-shared struct SharedRegion(ParentAllocator = NullAllocator,
+shared struct SharedRegion(ParentAllocator,
     uint minAlign = platformAlignment,
     Flag!"growDownwards" growDownwards = No.growDownwards)
 {
@@ -1016,45 +1199,36 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     {
         alias parent = ParentAllocator.instance;
     }
-    private shared void* _current, _begin, _end;
+    private shared SharedBorrowedRegion!(minAlign, growDownwards) _impl;
 
     private void* roundedBegin() const pure nothrow @trusted @nogc
     {
-        return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+        return _impl.roundedBegin;
     }
 
     private void* roundedEnd() const pure nothrow @trusted @nogc
     {
-        return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+        return _impl.roundedEnd;
     }
 
 
     /**
     Constructs a region backed by a user-provided store.
-    Assumes the memory was allocated with `ParentAllocator`
-    (if different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator)).
+    Assumes the memory was allocated with `ParentAllocator`.
 
     Params:
-        store = User-provided store backing up the region. If `ParentAllocator`
-        is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), memory is assumed to
-        have been allocated with `ParentAllocator`.
-        n = Bytes to allocate using `ParentAllocator`. This constructor is only
-        defined If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator). If
-        `parent.allocate(n)` returns `null`, the region will be initialized
-        as empty (correctly initialized but unable to allocate).
+        store = User-provided store backing up the region. Assumed to have been
+        allocated with `ParentAllocator`.
+        n = Bytes to allocate using `ParentAllocator`. If `parent.allocate(n)`
+        returns `null`, the region will be initialized as empty (correctly
+        initialized but unable to allocate).
     */
     this(ubyte[] store) pure nothrow @nogc
     {
-        _begin = cast(typeof(_begin)) store.ptr;
-        _end = cast(typeof(_end)) (store.ptr + store.length);
-        static if (growDownwards)
-            _current = cast(typeof(_current)) roundedEnd();
-        else
-            _current = cast(typeof(_current)) roundedBegin();
+        _impl = store;
     }
 
     /// Ditto
-    static if (!is(ParentAllocator == NullAllocator))
     this(size_t n)
     {
         this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
@@ -1065,7 +1239,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     */
     size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
     {
-        return n.roundUpToAlignment(alignment);
+        return _impl.goodAllocSize(n);
     }
 
     /**
@@ -1086,38 +1260,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     */
     void[] allocate(size_t n) pure nothrow @trusted @nogc
     {
-        import core.atomic : cas, atomicLoad;
-
-        if (n == 0) return null;
-        const rounded = goodAllocSize(n);
-
-        shared void* localCurrent, localNewCurrent;
-        static if (growDownwards)
-        {
-            do
-            {
-                localCurrent = atomicLoad(_current);
-                localNewCurrent = localCurrent - rounded;
-                if (localNewCurrent > localCurrent || localNewCurrent < _begin)
-                    return null;
-            } while (!cas(&_current, localCurrent, localNewCurrent));
-
-            return cast(void[]) localNewCurrent[0 .. n];
-        }
-        else
-        {
-            do
-            {
-                localCurrent = atomicLoad(_current);
-                localNewCurrent = localCurrent + rounded;
-                if (localNewCurrent < localCurrent || localNewCurrent > _end)
-                    return null;
-            } while (!cas(&_current, localCurrent, localNewCurrent));
-
-            return cast(void[]) localCurrent[0 .. n];
-        }
-
-        assert(0, "Unexpected error in SharedRegion.allocate");
+        return _impl.allocate(n);
     }
 
     /**
@@ -1131,97 +1274,30 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     */
     bool deallocate(void[] b) pure nothrow @nogc
     {
-        import core.atomic : cas, atomicLoad;
-
-        const rounded = goodAllocSize(b.length);
-        shared void* localCurrent, localNewCurrent;
-
-        // The cas is done only once, because only the last allocation can be reverted
-        localCurrent = atomicLoad(_current);
-        static if (growDownwards)
-        {
-            localNewCurrent = localCurrent + rounded;
-            if (b.ptr == localCurrent)
-                return cas(&_current, localCurrent, localNewCurrent);
-        }
-        else
-        {
-            localNewCurrent = localCurrent - rounded;
-            if (b.ptr == localNewCurrent)
-                return cas(&_current, localCurrent, localNewCurrent);
-        }
-
-        return false;
-    }
-
-    /**
-    Deallocates all memory allocated by this region, which can be subsequently
-    reused for new allocations.
-    */
-    bool deallocateAll() pure nothrow @nogc
-    {
-        import core.atomic : atomicStore;
-        static if (growDownwards)
-        {
-            atomicStore(_current, cast(shared(void*)) roundedEnd());
-        }
-        else
-        {
-            atomicStore(_current, cast(shared(void*)) roundedBegin());
-        }
-        return true;
+        return _impl.deallocate(b);
     }
 
-    /**
-    Allocates `n` bytes of memory aligned at alignment `a`.
-    Params:
-        n = number of bytes to allocate
-        a = alignment for the allocated block
-
-    Returns:
-        Either a suitable block of `n` bytes aligned at `a`, or `null`.
-    */
-    void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
-    {
-        import core.atomic : cas, atomicLoad;
-        import std.math.traits : isPowerOf2;
-
-        assert(a.isPowerOf2);
-        if (n == 0) return null;
-
-        const rounded = goodAllocSize(n);
-        shared void* localCurrent, localNewCurrent;
-
-        static if (growDownwards)
-        {
-            do
-            {
-                localCurrent = atomicLoad(_current);
-                auto alignedCurrent = cast(void*)(localCurrent - rounded);
-                localNewCurrent = cast(shared(void*)) alignedCurrent.alignDownTo(a);
-                if (alignedCurrent > localCurrent || localNewCurrent > alignedCurrent ||
-                    localNewCurrent < _begin)
-                    return null;
-            } while (!cas(&_current, localCurrent, localNewCurrent));
-
-            return cast(void[]) localNewCurrent[0 .. n];
-        }
-        else
-        {
-            do
-            {
-                localCurrent = atomicLoad(_current);
-                auto alignedCurrent = alignUpTo(cast(void*) localCurrent, a);
-                localNewCurrent = cast(shared(void*)) (alignedCurrent + rounded);
-                if (alignedCurrent < localCurrent || localNewCurrent < alignedCurrent ||
-                    localNewCurrent > _end)
-                    return null;
-            } while (!cas(&_current, localCurrent, localNewCurrent));
-
-            return cast(void[]) (localNewCurrent - rounded)[0 .. n];
-        }
+    /**
+    Deallocates all memory allocated by this region, which can be subsequently
+    reused for new allocations.
+    */
+    bool deallocateAll() pure nothrow @nogc
+    {
+        return _impl.deallocateAll;
+    }
+
+    /**
+    Allocates `n` bytes of memory aligned at alignment `a`.
+    Params:
+        n = number of bytes to allocate
+        a = alignment for the allocated block
 
-        assert(0, "Unexpected error in SharedRegion.alignedAllocate");
+    Returns:
+        Either a suitable block of `n` bytes aligned at `a`, or `null`.
+    */
+    void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
+    {
+        return _impl.alignedAllocate(n, a);
     }
 
     /**
@@ -1236,7 +1312,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     */
     Ternary owns(const void[] b) const pure nothrow @trusted @nogc
     {
-        return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+        return _impl.owns(b);
     }
 
     /**
@@ -1245,25 +1321,17 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     */
     Ternary empty() const pure nothrow @safe @nogc
     {
-        import core.atomic : atomicLoad;
-
-        auto localCurrent = atomicLoad(_current);
-        static if (growDownwards)
-            return Ternary(localCurrent == roundedEnd());
-        else
-            return Ternary(localCurrent == roundedBegin());
+        return _impl.empty;
     }
 
     /**
-    If `ParentAllocator` is not $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator) and defines `deallocate`,
-    the region defines a destructor that uses `ParentAllocator.deallocate` to free the
-    memory chunk.
+    If `ParentAllocator` defines `deallocate`, the region defines a destructor
+    that uses `ParentAllocator.deallocate` to free the memory chunk.
     */
-    static if (!is(ParentAllocator == NullAllocator)
-        && hasMember!(ParentAllocator, "deallocate"))
+    static if (hasMember!(ParentAllocator, "deallocate"))
     ~this()
     {
-        parent.deallocate(cast(void[]) _begin[0 .. _end - _begin]);
+        with (_impl) parent.deallocate(cast(void[]) _begin[0 .. _end - _begin]);
     }
 }
 
@@ -1397,3 +1465,250 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
     testAlloc(a1, true);
     testAlloc(a2, false);
 }
+
+/**
+A `SharedBorrowedRegion` allocates directly from a user-provided block of memory.
+
+Unlike a `SharedRegion`, a `SharedBorrowedRegion` does not own the memory it
+allocates from and will not deallocate that memory upon destruction. Instead,
+it is the user's responsibility to ensure that the memory is properly disposed
+of.
+
+In all other respects, a `SharedBorrowedRegion` behaves exactly like a `SharedRegion`.
+*/
+shared struct SharedBorrowedRegion(uint minAlign = platformAlignment,
+    Flag!"growDownwards" growDownwards = No.growDownwards)
+{
+    static assert(minAlign.isGoodStaticAlignment);
+
+    import std.typecons : Ternary;
+
+    // state
+    private void* _current, _begin, _end;
+
+    private void* roundedBegin() shared const pure nothrow @trusted @nogc
+    {
+        return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+    }
+
+    private void* roundedEnd() shared const pure nothrow @trusted @nogc
+    {
+        return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+    }
+
+    /**
+    Constructs a region backed by a user-provided store.
+
+    Params:
+        store = User-provided store backing up the region. Must not be aliased.
+    */
+    this(ubyte[] store) shared pure nothrow @nogc
+    {
+        _begin = cast(typeof(_begin)) store.ptr;
+        _end = cast(typeof(_end)) (store.ptr + store.length);
+        static if (growDownwards)
+            _current = cast(typeof(_current)) roundedEnd();
+        else
+            _current = cast(typeof(_current)) roundedBegin();
+    }
+
+    /*
+    TODO: The postblit of `SharedBorrowedRegion` should be disabled because
+    such objects should not be copied around naively.
+    */
+
+    /**
+    Rounds the given size to a multiple of the `alignment`
+    */
+    size_t goodAllocSize(size_t n) shared const pure nothrow @safe @nogc
+    {
+        return n.roundUpToAlignment(alignment);
+    }
+
+    /**
+    Alignment offered.
+    */
+    alias alignment = minAlign;
+
+    /**
+    Allocates `n` bytes of memory. The allocation is served by atomically incrementing
+    a pointer which keeps track of the current used space.
+
+    Params:
+        n = number of bytes to allocate
+
+    Returns:
+        A properly-aligned buffer of size `n`, or `null` if request could not
+        be satisfied.
+    */
+    void[] allocate(size_t n) shared pure nothrow @trusted @nogc
+    {
+        import core.atomic : cas, atomicLoad;
+
+        if (n == 0) return null;
+        const rounded = goodAllocSize(n);
+
+        shared void* localCurrent, localNewCurrent;
+        static if (growDownwards)
+        {
+            do
+            {
+                localCurrent = atomicLoad(_current);
+                localNewCurrent = localCurrent - rounded;
+                if (localNewCurrent > localCurrent || localNewCurrent < _begin)
+                    return null;
+            } while (!cas(&_current, localCurrent, localNewCurrent));
+
+            return cast(void[]) localNewCurrent[0 .. n];
+        }
+        else
+        {
+            do
+            {
+                localCurrent = atomicLoad(_current);
+                localNewCurrent = localCurrent + rounded;
+                if (localNewCurrent < localCurrent || localNewCurrent > _end)
+                    return null;
+            } while (!cas(&_current, localCurrent, localNewCurrent));
+
+            return cast(void[]) localCurrent[0 .. n];
+        }
+
+        assert(0, "Unexpected error in SharedBorrowedRegion.allocate");
+    }
+
+    /**
+    Allocates `n` bytes of memory aligned at alignment `a`.
+
+    Params:
+        n = number of bytes to allocate
+        a = alignment for the allocated block
+
+    Returns:
+        Either a suitable block of `n` bytes aligned at `a`, or `null`.
+    */
+    void[] alignedAllocate(size_t n, uint a) shared pure nothrow @trusted @nogc
+    {
+        import core.atomic : cas, atomicLoad;
+        import std.math.traits : isPowerOf2;
+
+        assert(a.isPowerOf2);
+        if (n == 0) return null;
+
+        const rounded = goodAllocSize(n);
+        shared void* localCurrent, localNewCurrent;
+
+        static if (growDownwards)
+        {
+            do
+            {
+                localCurrent = atomicLoad(_current);
+                auto alignedCurrent = cast(void*)(localCurrent - rounded);
+                localNewCurrent = cast(shared(void*)) alignedCurrent.alignDownTo(a);
+                if (alignedCurrent > localCurrent || localNewCurrent > alignedCurrent ||
+                    localNewCurrent < _begin)
+                    return null;
+            } while (!cas(&_current, localCurrent, localNewCurrent));
+
+            return cast(void[]) localNewCurrent[0 .. n];
+        }
+        else
+        {
+            do
+            {
+                localCurrent = atomicLoad(_current);
+                auto alignedCurrent = alignUpTo(cast(void*) localCurrent, a);
+                localNewCurrent = cast(shared(void*)) (alignedCurrent + rounded);
+                if (alignedCurrent < localCurrent || localNewCurrent < alignedCurrent ||
+                    localNewCurrent > _end)
+                    return null;
+            } while (!cas(&_current, localCurrent, localNewCurrent));
+
+            return cast(void[]) (localNewCurrent - rounded)[0 .. n];
+        }
+
+        assert(0, "Unexpected error in SharedBorrowedRegion.alignedAllocate");
+    }
+
+    /**
+    Deallocates `b`. This works only if `b` was obtained as the last call
+    to `allocate`; otherwise (i.e. another allocation has occurred since) it
+    does nothing.
+
+    Params:
+        b = Block previously obtained by a call to `allocate` against this
+        allocator (`null` is allowed).
+    */
+    bool deallocate(void[] b) shared pure nothrow @nogc
+    {
+        import core.atomic : cas, atomicLoad;
+
+        const rounded = goodAllocSize(b.length);
+        shared void* localCurrent, localNewCurrent;
+
+        // The cas is done only once, because only the last allocation can be reverted
+        localCurrent = atomicLoad(_current);
+        static if (growDownwards)
+        {
+            localNewCurrent = localCurrent + rounded;
+            if (b.ptr == localCurrent)
+                return cas(&_current, localCurrent, localNewCurrent);
+        }
+        else
+        {
+            localNewCurrent = localCurrent - rounded;
+            if (b.ptr == localNewCurrent)
+                return cas(&_current, localCurrent, localNewCurrent);
+        }
+
+        return false;
+    }
+
+    /**
+    Deallocates all memory allocated by this region, which can be subsequently
+    reused for new allocations.
+    */
+    bool deallocateAll() shared pure nothrow @nogc
+    {
+        import core.atomic : atomicStore;
+        static if (growDownwards)
+        {
+            atomicStore(_current, cast(shared(void*)) roundedEnd());
+        }
+        else
+        {
+            atomicStore(_current, cast(shared(void*)) roundedBegin());
+        }
+        return true;
+    }
+
+    /**
+    Queries whether `b` has been allocated with this region.
+
+    Params:
+        b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns
+        `false`).
+
+    Returns:
+        `true` if `b` has been allocated with this region, `false` otherwise.
+    */
+    Ternary owns(const void[] b) shared const pure nothrow @trusted @nogc
+    {
+        return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+    }
+
+    /**
+    Returns `Ternary.yes` if no memory has been allocated in this region,
+    `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
+    */
+    Ternary empty() shared const pure nothrow @safe @nogc
+    {
+        import core.atomic : atomicLoad;
+
+        auto localCurrent = atomicLoad(_current);
+        static if (growDownwards)
+            return Ternary(localCurrent == roundedEnd());
+        else
+            return Ternary(localCurrent == roundedBegin());
+    }
+}
index 8fc7584f796de52f4fc0a13cd81d43dc2f477522..96859b0762f90e882abce3e62fcb9c9e98e768cc 100644 (file)
@@ -259,10 +259,10 @@ version (StdUnittest)
 // Test that deallocateAll infers from parent
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
 
-    ScopedAllocator!(Region!()) a;
-    a.parent.parent = Region!()(new ubyte[1024 * 64]);
+    ScopedAllocator!(BorrowedRegion!()) a;
+    a.parent.parent = BorrowedRegion!()(new ubyte[1024 * 64]);
     auto b = a.allocate(42);
     assert(b.length == 42);
     assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
index 655db4567ce1825a49f253f650b3f59885ed6a29..ff089bddc13f79a66efc8ba9fb2b67be5f106bab 100644 (file)
@@ -503,12 +503,12 @@ if (Args.length > 3)
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
     import std.typecons : Ternary;
 
-    auto a = Segregator!(10_240, Region!(), Region!())(
-                Region!()(new ubyte[4096 * 1024]),
-                Region!()(new ubyte[4096 * 1024]));
+    auto a = Segregator!(10_240, BorrowedRegion!(), BorrowedRegion!())(
+                BorrowedRegion!()(new ubyte[4096 * 1024]),
+                BorrowedRegion!()(new ubyte[4096 * 1024]));
 
     assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
     auto b = a.alignedAllocate(42, 8);
index d57b3edd16faa92d2cbc643de425247425c6474f..3770af10ceb33e97be8cef3a11206475df56cfe9 100644 (file)
@@ -845,9 +845,9 @@ public:
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
 
-    auto a = StatsCollector!(Region!(), Options.all, Options.all)(Region!()(new ubyte[1024 * 64]));
+    auto a = StatsCollector!(BorrowedRegion!(), Options.all, Options.all)(BorrowedRegion!()(new ubyte[1024 * 64]));
     auto b = a.allocate(42);
     assert(b.length == 42);
     // Test that reallocate infers from parent
@@ -859,9 +859,9 @@ public:
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
 
-    auto a = StatsCollector!(Region!(), Options.all)(Region!()(new ubyte[1024 * 64]));
+    auto a = StatsCollector!(BorrowedRegion!(), Options.all)(BorrowedRegion!()(new ubyte[1024 * 64]));
     auto b = a.alignedAllocate(42, 128);
     assert(b.length == 42);
     assert(b.ptr.alignedAt(128));
index 62f848f11fcdee3afdf4b27c88fb3a0364d6d07a..217792676b509de91cce7b59e061b4849c2ad6c7 100644 (file)
@@ -547,24 +547,24 @@ nothrow:
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
     import std.conv : emplace;
 
-    auto reg = Region!()(new ubyte[1024]);
-    auto state = reg.allocate(stateSize!(CAllocatorImpl!(Region!(), Yes.indirect)));
-    auto regObj = emplace!(CAllocatorImpl!(Region!(), Yes.indirect))(state, &reg);
+    auto reg = BorrowedRegion!()(new ubyte[1024]);
+    auto state = reg.allocate(stateSize!(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect)));
+    auto regObj = emplace!(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(state, &reg);
 
     auto rcalloc = RCIAllocator(regObj);
     auto b = rcalloc.allocate(10);
     assert(b.length == 10);
 
     // The reference counting is zero based
-    assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
+    assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
     {
         auto rca2 = rcalloc;
-        assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
+        assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
     }
-    assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
+    assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
 }
 
 @system unittest
index d6cac41ef04cc2bf348053e4ca75112ccf954ad0..8957089de89b6c910bffe6b0b7da75f25074827e 100644 (file)
@@ -3714,7 +3714,7 @@ assert(!de2.isFile);
         @property bool isSymlink() scope;
 
         /++
-            Returns the size of the the file represented by this `DirEntry`
+            Returns the size of the file represented by this `DirEntry`
             in bytes.
           +/
         @property ulong size() scope;
index 3f6f33adf6e9fc326fb628a6f4b937b84eb334a0..d83f0281e594d8f3572eb5958e7c06542eb31d8f 100644 (file)
@@ -550,7 +550,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
 Andrei Alexandrescu), and Kenji Hara
 
-Source: $(PHOBOSSRC std/format.d)
+Source: $(PHOBOSSRC std/format/package.d)
  */
 module std.format;
 
index 7f72234d64071d573387ce5631bf62fe47a52659..703ecb1e6b35e2416dd746874d9c8a0215753582 100644 (file)
@@ -631,7 +631,7 @@ enum real BETA_BIGINV = 1.084202172485504434007e-19L;
  * betaIncomplete(a, b, x) = &Gamma;(a+b)/(&Gamma;(a) &Gamma;(b)) *
  * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t),b-1) dt
  *
- * and is the same as the the cumulative distribution function.
+ * and is the same as the cumulative distribution function.
  *
  * The domain of definition is 0 <= x <= 1.  In this
  * implementation a and b are restricted to positive values.
index be2bd8d175e56a7bb6e0a9fbc7d3966135ce5db8..846f6ee214ab4e6d07e529c4b5d2566f12f66bca 100644 (file)
@@ -704,7 +704,7 @@ abstract class Logger
     /** This template provides the log functions for the `Logger` `class`
     with the `LogLevel` encoded in the function name.
 
-    For further information see the the two functions defined inside of this
+    For further information see the two functions defined inside of this
     template.
 
     The aliases following this template create the public names of these log
@@ -1446,7 +1446,7 @@ that the returned reference is only a current snapshot and in the following
 code, you must make sure no other thread reassigns to it between reading and
 writing `sharedLog`.
 
-`sharedLog` is only thread-safe if the the used `Logger` is thread-safe.
+`sharedLog` is only thread-safe if the used `Logger` is thread-safe.
 The default `Logger` is thread-safe.
 -------------
 if (sharedLog !is myLogger)
@@ -1559,10 +1559,21 @@ class StdForwardLogger : Logger
         }
     }
 
+    auto oldSharedLog = sharedLog;
+
     sharedLog = new shared RaceLogger;
-    scope(exit) { sharedLog = null; }
-    () @trusted { new Thread(() { log("foo"); }).start(); }();
+    scope(exit)
+    {
+        sharedLog = oldSharedLog;
+    }
+    Thread toWaitFor;
+    () @trusted { toWaitFor = new Thread(() { log("foo"); }).start(); }();
     log("bar");
+
+    () @trusted
+    {
+        toWaitFor.join();
+    }();
 }
 
 /** This `LogLevel` is unqiue to every thread.
@@ -1897,7 +1908,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe
         assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
     } ();
     lineNumber = __LINE__ - 2;
-    assert(l.msg == msg.format("Yet"));
+    assert(l.msg == msg.format("Yet"), l.msg);
     assert(l.line == lineNumber);
     assert(l.logLevel == LogLevel.all);
 
index 7ea2cebb30f44517cf3003667cdf5e4ef5721567..23182790be7e60dd71d4d19f9f5f7806ad8f761f 100644 (file)
@@ -1973,7 +1973,7 @@ private mixin template Protocol()
     }
 
     /**
-     * The network interface to use in form of the the IP of the interface.
+     * The network interface to use in form of the IP of the interface.
      *
      * Example:
      * ----
@@ -2706,7 +2706,7 @@ struct HTTP
         @property void dnsTimeout(Duration d);
 
         /**
-         * The network interface to use in form of the the IP of the interface.
+         * The network interface to use in form of the IP of the interface.
          *
          * Example:
          * ----
@@ -3486,7 +3486,7 @@ struct FTP
         @property void dnsTimeout(Duration d);
 
         /**
-         * The network interface to use in form of the the IP of the interface.
+         * The network interface to use in form of the IP of the interface.
          *
          * Example:
          * ----
@@ -3912,7 +3912,7 @@ struct SMTP
         @property void dnsTimeout(Duration d);
 
         /**
-         * The network interface to use in form of the the IP of the interface.
+         * The network interface to use in form of the IP of the interface.
          *
          * Example:
          * ----
index 96d20c233d83a1b8901fb92a54810ad1dc556bbe..df7ac39b4e445e64b0c49e3f6be9c4d4750f13ea 100644 (file)
@@ -1124,8 +1124,8 @@ public:
  *
  * References: "On Enclosing Simple Roots of Nonlinear Equations",
  * G. Alefeld, F.A. Potra, Yixun Shi, Mathematics of Computation 61,
- * pp733-744 (1993).  Fortran code available from $(HTTP
- * www.netlib.org,www.netlib.org) as algorithm TOMS478.
+ * pp733-744 (1993).  Fortran code available from
+ * $(HTTP www.netlib.org,www.netlib.org) as algorithm TOMS478.
  *
  */
 T findRoot(T, DF, DT)(scope DF f, const T a, const T b,
index de180fcc548d4375751b022374ffba7eaa3cf2c3..4b5a7efe07f2e742eeda26ac52de1915c01af8d2 100644 (file)
@@ -3357,8 +3357,10 @@ in
 {
     // Verify that pattern[] is valid
     import std.algorithm.searching : balancedParens;
-    assert(balancedParens(pattern, '[', ']', 0));
-    assert(balancedParens(pattern, '{', '}', 0));
+    import std.utf : byUTF;
+
+    assert(balancedParens(pattern.byUTF!C, '[', ']', 0));
+    assert(balancedParens(pattern.byUTF!C, '{', '}', 0));
 }
 do
 {
@@ -3959,7 +3961,7 @@ string expandTilde(string inputPath) @safe nothrow
     version (Posix)
     {
         import core.exception : onOutOfMemoryError;
-        import core.stdc.errno : errno, ERANGE;
+        import core.stdc.errno : errno, EBADF, ENOENT, EPERM, ERANGE, ESRCH;
         import core.stdc.stdlib : malloc, free, realloc;
 
         /*  Joins a path from a C string to the remainder of path.
@@ -4065,7 +4067,7 @@ string expandTilde(string inputPath) @safe nothrow
                 char[] extra_memory;
 
                 passwd result;
-                while (1)
+                loop: while (1)
                 {
                     extra_memory.length += extra_memory_size;
 
@@ -4088,10 +4090,23 @@ string expandTilde(string inputPath) @safe nothrow
                         break;
                     }
 
-                    if (errno != ERANGE &&
+                    switch (errno)
+                    {
+                        case ERANGE:
                         // On BSD and OSX, errno can be left at 0 instead of set to ERANGE
-                        errno != 0)
-                        onOutOfMemoryError();
+                        case 0:
+                            break;
+
+                        case ENOENT:
+                        case ESRCH:
+                        case EBADF:
+                        case EPERM:
+                            // The given name or uid was not found.
+                            break loop;
+
+                        default:
+                            onOutOfMemoryError();
+                    }
 
                     // extra_memory isn't large enough
                     import core.checkedint : mulu;
index 31f58fa5fa9e721f903c4e49d1a2a8997811812b..cdab401a1f28447fe8a4fc840802373f44e66412 100644 (file)
@@ -172,7 +172,7 @@ Returns:
 enum bool isInputRange(R) =
     is(typeof(R.init) == R)
     && is(ReturnType!((R r) => r.empty) == bool)
-    && is(typeof((return ref R r) => r.front))
+    && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front)))
     && !is(ReturnType!((R r) => r.front) == void)
     && is(typeof((R r) => r.popFront));
 
@@ -227,6 +227,17 @@ enum bool isInputRange(R) =
     }
     static assert(!isInputRange!VoidFront);
 }
+// https://issues.dlang.org/show_bug.cgi?id=16034
+@safe unittest
+{
+    struct One
+    {
+        int entry = 1;
+        @disable this(this);
+    }
+
+    assert(isInputRange!(One[]));
+}
 
 @safe unittest
 {
index 6ec798581947070092b9394e316b849652bdd3da..fb2c2d4225300a52d3eac860f3231821a467fb29 100644 (file)
@@ -2942,7 +2942,7 @@ public:
      * Calling `shutdown` before `close` is recommended
      * for connection-oriented sockets.
      */
-    void close() @trusted nothrow @nogc
+    void close() scope @trusted nothrow @nogc
     {
         _close(sock);
         sock = socket_t.init;
@@ -3641,7 +3641,7 @@ class UdpSocket: Socket
             {
                 checkAttributes!q{nothrow @nogc @trusted};
             }
-            nothrow @nogc @trusted void close()
+            nothrow @nogc @trusted scope void close()
             {
                 checkAttributes!q{nothrow @nogc @trusted};
             }
index a1fe962906bde5380c0573633a7d2f2e9edf5477..cd1a356eda10fa3b1418872f0c3637101562bdba 100644 (file)
@@ -85,8 +85,7 @@ else version (CRuntime_Musl)
 }
 else version (CRuntime_UClibc)
 {
-    // uClibc supports GCC IO
-    version = GCC_IO;
+    version = GENERIC_IO;
 }
 else version (OSX)
 {
@@ -589,7 +588,7 @@ Throws: `ErrnoException` if the file could not be opened.
         detach();
     }
 
-    this(this) @safe nothrow
+    this(this) @safe pure nothrow @nogc
     {
         if (!_p) return;
         assert(atomicLoad(_p.refs));
index 160665c6a3102093735d34be6e9ea653ef106eb3..ee2d73a1233428336d0608e72615b5026030ecc5 100644 (file)
@@ -753,23 +753,6 @@ public:
         }
     }
 
-    invariant
-    {
-        this.match!((ref value) {
-            static if (is(typeof(value) == class))
-            {
-                if (value !is null)
-                {
-                    assert(value);
-                }
-            }
-            else static if (is(typeof(value) == struct))
-            {
-                assert(&value);
-            }
-        });
-    }
-
     // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
     version (StdDdoc)
     {
@@ -1330,36 +1313,6 @@ version (D_BetterC) {} else
     }));
 }
 
-// Types with invariants
-// Disabled in BetterC due to use of exceptions
-version (D_BetterC) {} else
-version (D_Invariants)
-@system unittest
-{
-    import std.exception : assertThrown;
-    import core.exception : AssertError;
-
-    struct S
-    {
-        int i;
-        invariant { assert(i >= 0); }
-    }
-
-    class C
-    {
-        int i;
-        invariant { assert(i >= 0); }
-    }
-
-    SumType!S x;
-    x.match!((ref v) { v.i = -1; });
-    assertThrown!AssertError(assert(&x));
-
-    SumType!C y = new C();
-    y.match!((ref v) { v.i = -1; });
-    assertThrown!AssertError(assert(&y));
-}
-
 // Calls value postblit on self-assignment
 @safe unittest
 {
index c7cdc24e4d59275ac114b6b768214d036135a89c..094628b559cf656501b4dbcf1a7238dc75df9fc3 100644 (file)
@@ -3793,8 +3793,28 @@ Params:
                 sink.formatValue(_value, fmt);
             }
         }
+
+        void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const
+        {
+            if (isNull)
+            {
+                sink.formatValue("Nullable.null", fmt);
+            }
+            else
+            {
+                sink.formatValue(_value, fmt);
+            }
+        }
     }
 
+@system unittest
+{
+    import std.conv : to;
+
+    const Nullable!(ulong, 0) x = 1;
+    assert(x.to!string == "1");
+}
+
 /**
 Check if `this` is in the null state.
 
@@ -4320,8 +4340,28 @@ Params:
                 sink.formatValue(*_value, fmt);
             }
         }
+
+        void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const
+        {
+            if (isNull)
+            {
+                sink.formatValue("Nullable.null", fmt);
+            }
+            else
+            {
+                sink.formatValue(*_value, fmt);
+            }
+        }
     }
 
+@system unittest
+{
+    import std.conv : to;
+
+    const NullableRef!(ulong) x = new ulong(1);
+    assert(x.to!string == "1");
+}
+
 /**
 Binds the internal state to `value`.
 
index e12a70cfe80ba7255e17fa4645ea1aec7d6b8366..8a032aaa514740560c9c94009d5253e8c9cb9dbb 100644 (file)
@@ -426,7 +426,7 @@ $(TR $(TD Building blocks) $(TD
     $(SECTION Construction of lookup tables)
     $(P The Unicode standard describes a set of algorithms that
         depend on having the ability to quickly look up various properties
-        of a code point. Given the the codespace of about 1 million $(CODEPOINTS),
+        of a code point. Given the codespace of about 1 million $(CODEPOINTS),
         it is not a trivial task to provide a space-efficient solution for
         the multitude of properties.
     )
index d22dac8b15daa3bab27dab57c624c90bde37516d..8d94e121ff2170246e08c5441eeae7973c81f950 100644 (file)
@@ -3571,7 +3571,7 @@ enum dchar replacementDchar = '\uFFFD';
  *      of characters (including strings) or a type that implicitly converts to a string type.
  * Returns:
  *      If `r` is not an auto-decodable string (i.e. a narrow string or a
- *      user-defined type that implicits converts to a string type), then `r`
+ *      user-defined type that implicitly converts to a string type), then `r`
  *      is returned.
  *
  *      Otherwise, `r` is converted to its corresponding string type (if it's
diff --git a/libphobos/src/std/xml.d b/libphobos/src/std/xml.d
deleted file mode 100644 (file)
index fdfdc3f..0000000
+++ /dev/null
@@ -1,3113 +0,0 @@
-// Written in the D programming language.
-
-/**
-$(RED Warning: This module is considered out-dated and not up to Phobos'
-      current standards. It will be removed from Phobos in 2.101.0.
-      If you still need it, go to $(LINK https://github.com/DigitalMars/undeaD))
- */
-
-/*
-Classes and functions for creating and parsing XML
-
-The basic architecture of this module is that there are standalone functions,
-classes for constructing an XML document from scratch (Tag, Element and
-Document), and also classes for parsing a pre-existing XML file (ElementParser
-and DocumentParser). The parsing classes <i>may</i> be used to build a
-Document, but that is not their primary purpose. The handling capabilities of
-DocumentParser and ElementParser are sufficiently customizable that you can
-make them do pretty much whatever you want.
-
-Example: This example creates a DOM (Document Object Model) tree
-    from an XML file.
-------------------------------------------------------------------------------
-import std.xml;
-import std.stdio;
-import std.string;
-import std.file;
-
-// books.xml is used in various samples throughout the Microsoft XML Core
-// Services (MSXML) SDK.
-//
-// See http://msdn2.microsoft.com/en-us/library/ms762271(VS.85).aspx
-
-void main()
-{
-    string s = cast(string) std.file.read("books.xml");
-
-    // Check for well-formedness
-    check(s);
-
-    // Make a DOM tree
-    auto doc = new Document(s);
-
-    // Plain-print it
-    writeln(doc);
-}
-------------------------------------------------------------------------------
-
-Example: This example does much the same thing, except that the file is
-    deconstructed and reconstructed by hand. This is more work, but the
-    techniques involved offer vastly more power.
-------------------------------------------------------------------------------
-import std.xml;
-import std.stdio;
-import std.string;
-
-struct Book
-{
-    string id;
-    string author;
-    string title;
-    string genre;
-    string price;
-    string pubDate;
-    string description;
-}
-
-void main()
-{
-    string s = cast(string) std.file.read("books.xml");
-
-    // Check for well-formedness
-    check(s);
-
-    // Take it apart
-    Book[] books;
-
-    auto xml = new DocumentParser(s);
-    xml.onStartTag["book"] = (ElementParser xml)
-    {
-        Book book;
-        book.id = xml.tag.attr["id"];
-
-        xml.onEndTag["author"]       = (in Element e) { book.author      = e.text(); };
-        xml.onEndTag["title"]        = (in Element e) { book.title       = e.text(); };
-        xml.onEndTag["genre"]        = (in Element e) { book.genre       = e.text(); };
-        xml.onEndTag["price"]        = (in Element e) { book.price       = e.text(); };
-        xml.onEndTag["publish-date"] = (in Element e) { book.pubDate     = e.text(); };
-        xml.onEndTag["description"]  = (in Element e) { book.description = e.text(); };
-
-        xml.parse();
-
-        books ~= book;
-    };
-    xml.parse();
-
-    // Put it back together again;
-    auto doc = new Document(new Tag("catalog"));
-    foreach (book;books)
-    {
-        auto element = new Element("book");
-        element.tag.attr["id"] = book.id;
-
-        element ~= new Element("author",      book.author);
-        element ~= new Element("title",       book.title);
-        element ~= new Element("genre",       book.genre);
-        element ~= new Element("price",       book.price);
-        element ~= new Element("publish-date",book.pubDate);
-        element ~= new Element("description", book.description);
-
-        doc ~= element;
-    }
-
-    // Pretty-print it
-    writefln(join(doc.pretty(3),"\n"));
-}
--------------------------------------------------------------------------------
-Copyright: Copyright Janice Caron 2008 - 2009.
-License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors:   Janice Caron
-Source:    $(PHOBOSSRC std/xml.d)
-*/
-/*
-         Copyright Janice Caron 2008 - 2009.
-Distributed under the Boost Software License, Version 1.0.
-   (See accompanying file LICENSE_1_0.txt or copy at
-         http://www.boost.org/LICENSE_1_0.txt)
-*/
-deprecated("Will be removed from Phobos in 2.101.0. If you still need it, go to https://github.com/DigitalMars/undeaD")
-module std.xml;
-
-enum cdata = "<![CDATA[";
-
-/*
- * Returns true if the character is a character according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isChar(dchar c) @safe @nogc pure nothrow // rule 2
-{
-    if (c <= 0xD7FF)
-    {
-        if (c >= 0x20)
-            return true;
-        switch (c)
-        {
-        case 0xA:
-        case 0x9:
-        case 0xD:
-            return true;
-        default:
-            return false;
-        }
-    }
-    else if (0xE000 <= c && c <= 0x10FFFF)
-    {
-        if ((c & 0x1FFFFE) != 0xFFFE) // U+FFFE and U+FFFF
-            return true;
-    }
-    return false;
-}
-
-@safe @nogc nothrow pure unittest
-{
-    assert(!isChar(cast(dchar) 0x8));
-    assert( isChar(cast(dchar) 0x9));
-    assert( isChar(cast(dchar) 0xA));
-    assert(!isChar(cast(dchar) 0xB));
-    assert(!isChar(cast(dchar) 0xC));
-    assert( isChar(cast(dchar) 0xD));
-    assert(!isChar(cast(dchar) 0xE));
-    assert(!isChar(cast(dchar) 0x1F));
-    assert( isChar(cast(dchar) 0x20));
-    assert( isChar('J'));
-    assert( isChar(cast(dchar) 0xD7FF));
-    assert(!isChar(cast(dchar) 0xD800));
-    assert(!isChar(cast(dchar) 0xDFFF));
-    assert( isChar(cast(dchar) 0xE000));
-    assert( isChar(cast(dchar) 0xFFFD));
-    assert(!isChar(cast(dchar) 0xFFFE));
-    assert(!isChar(cast(dchar) 0xFFFF));
-    assert( isChar(cast(dchar) 0x10000));
-    assert( isChar(cast(dchar) 0x10FFFF));
-    assert(!isChar(cast(dchar) 0x110000));
-
-    debug (stdxml_TestHardcodedChecks)
-    {
-        foreach (c; 0 .. dchar.max + 1)
-            assert(isChar(c) == lookup(CharTable, c));
-    }
-}
-
-/*
- * Returns true if the character is whitespace according to the XML standard
- *
- * Only the following characters are considered whitespace in XML - space, tab,
- * carriage return and linefeed
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isSpace(dchar c) @safe @nogc pure nothrow
-{
-    return c == '\u0020' || c == '\u0009' || c == '\u000A' || c == '\u000D';
-}
-
-/*
- * Returns true if the character is a digit according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isDigit(dchar c) @safe @nogc pure nothrow
-{
-    if (c <= 0x0039 && c >= 0x0030)
-        return true;
-    else
-        return lookup(DigitTable,c);
-}
-
-@safe @nogc nothrow pure unittest
-{
-    debug (stdxml_TestHardcodedChecks)
-    {
-        foreach (c; 0 .. dchar.max + 1)
-            assert(isDigit(c) == lookup(DigitTable, c));
-    }
-}
-
-/*
- * Returns true if the character is a letter according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isLetter(dchar c) @safe @nogc nothrow pure // rule 84
-{
-    return isIdeographic(c) || isBaseChar(c);
-}
-
-/*
- * Returns true if the character is an ideographic character according to the
- * XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isIdeographic(dchar c) @safe @nogc nothrow pure
-{
-    if (c == 0x3007)
-        return true;
-    if (c <= 0x3029 && c >= 0x3021 )
-        return true;
-    if (c <= 0x9FA5 && c >= 0x4E00)
-        return true;
-    return false;
-}
-
-@safe @nogc nothrow pure unittest
-{
-    assert(isIdeographic('\u4E00'));
-    assert(isIdeographic('\u9FA5'));
-    assert(isIdeographic('\u3007'));
-    assert(isIdeographic('\u3021'));
-    assert(isIdeographic('\u3029'));
-
-    debug (stdxml_TestHardcodedChecks)
-    {
-        foreach (c; 0 .. dchar.max + 1)
-            assert(isIdeographic(c) == lookup(IdeographicTable, c));
-    }
-}
-
-/*
- * Returns true if the character is a base character according to the XML
- * standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isBaseChar(dchar c) @safe @nogc nothrow pure
-{
-    return lookup(BaseCharTable,c);
-}
-
-/*
- * Returns true if the character is a combining character according to the
- * XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isCombiningChar(dchar c) @safe @nogc nothrow pure
-{
-    return lookup(CombiningCharTable,c);
-}
-
-/*
- * Returns true if the character is an extender according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *    c = the character to be tested
- */
-bool isExtender(dchar c) @safe @nogc nothrow pure
-{
-    return lookup(ExtenderTable,c);
-}
-
-/*
- * Encodes a string by replacing all characters which need to be escaped with
- * appropriate predefined XML entities.
- *
- * encode() escapes certain characters (ampersand, quote, apostrophe, less-than
- * and greater-than), and similarly, decode() unescapes them. These functions
- * are provided for convenience only. You do not need to use them when using
- * the std.xml classes, because then all the encoding and decoding will be done
- * for you automatically.
- *
- * If the string is not modified, the original will be returned.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *      s = The string to be encoded
- *
- * Returns: The encoded string
- *
- * Example:
- * --------------
- * writefln(encode("a > b")); // writes "a &gt; b"
- * --------------
- */
-S encode(S)(S s)
-{
-    import std.array : appender;
-
-    string r;
-    size_t lastI;
-    auto result = appender!S();
-
-    foreach (i, c; s)
-    {
-        switch (c)
-        {
-        case '&':  r = "&amp;"; break;
-        case '"':  r = "&quot;"; break;
-        case '\'': r = "&apos;"; break;
-        case '<':  r = "&lt;"; break;
-        case '>':  r = "&gt;"; break;
-        default: continue;
-        }
-        // Replace with r
-        result.put(s[lastI .. i]);
-        result.put(r);
-        lastI = i + 1;
-    }
-
-    if (!result.data.ptr) return s;
-    result.put(s[lastI .. $]);
-    return result.data;
-}
-
-@safe pure unittest
-{
-    auto s = "hello";
-    assert(encode(s) is s);
-    assert(encode("a > b") == "a &gt; b", encode("a > b"));
-    assert(encode("a < b") == "a &lt; b");
-    assert(encode("don't") == "don&apos;t");
-    assert(encode("\"hi\"") == "&quot;hi&quot;", encode("\"hi\""));
-    assert(encode("cat & dog") == "cat &amp; dog");
-}
-
-/*
- * Mode to use for decoding.
- *
- * $(DDOC_ENUM_MEMBERS NONE) Do not decode
- * $(DDOC_ENUM_MEMBERS LOOSE) Decode, but ignore errors
- * $(DDOC_ENUM_MEMBERS STRICT) Decode, and throw exception on error
- */
-enum DecodeMode
-{
-    NONE, LOOSE, STRICT
-}
-
-/*
- * Decodes a string by unescaping all predefined XML entities.
- *
- * encode() escapes certain characters (ampersand, quote, apostrophe, less-than
- * and greater-than), and similarly, decode() unescapes them. These functions
- * are provided for convenience only. You do not need to use them when using
- * the std.xml classes, because then all the encoding and decoding will be done
- * for you automatically.
- *
- * This function decodes the entities &amp;amp;, &amp;quot;, &amp;apos;,
- * &amp;lt; and &amp;gt,
- * as well as decimal and hexadecimal entities such as &amp;#x20AC;
- *
- * If the string does not contain an ampersand, the original will be returned.
- *
- * Note that the "mode" parameter can be one of DecodeMode.NONE (do not
- * decode), DecodeMode.LOOSE (decode, but ignore errors), or DecodeMode.STRICT
- * (decode, and throw a DecodeException in the event of an error).
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- *      s = The string to be decoded
- *      mode = (optional) Mode to use for decoding. (Defaults to LOOSE).
- *
- * Throws: DecodeException if mode == DecodeMode.STRICT and decode fails
- *
- * Returns: The decoded string
- *
- * Example:
- * --------------
- * writefln(decode("a &gt; b")); // writes "a > b"
- * --------------
- */
-string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
-{
-    import std.algorithm.searching : startsWith;
-
-    if (mode == DecodeMode.NONE) return s;
-
-    string buffer;
-    foreach (ref i; 0 .. s.length)
-    {
-        char c = s[i];
-        if (c != '&')
-        {
-            if (buffer.length != 0) buffer ~= c;
-        }
-        else
-        {
-            if (buffer.length == 0)
-            {
-                buffer = s[0 .. i].dup;
-            }
-            if (startsWith(s[i..$],"&#"))
-            {
-                try
-                {
-                    dchar d;
-                    string t = s[i..$];
-                    checkCharRef(t, d);
-                    char[4] temp;
-                    import std.utf : encode;
-                    buffer ~= temp[0 .. encode(temp, d)];
-                    i = s.length - t.length - 1;
-                }
-                catch (Err e)
-                {
-                    if (mode == DecodeMode.STRICT)
-                        throw new DecodeException("Unescaped &");
-                    buffer ~= '&';
-                }
-            }
-            else if (startsWith(s[i..$],"&amp;" )) { buffer ~= '&';  i += 4; }
-            else if (startsWith(s[i..$],"&quot;")) { buffer ~= '"';  i += 5; }
-            else if (startsWith(s[i..$],"&apos;")) { buffer ~= '\''; i += 5; }
-            else if (startsWith(s[i..$],"&lt;"  )) { buffer ~= '<';  i += 3; }
-            else if (startsWith(s[i..$],"&gt;"  )) { buffer ~= '>';  i += 3; }
-            else
-            {
-                if (mode == DecodeMode.STRICT)
-                    throw new DecodeException("Unescaped &");
-                buffer ~= '&';
-            }
-        }
-    }
-    return (buffer.length == 0) ? s : buffer;
-}
-
-@safe pure unittest
-{
-    void assertNot(string s) pure
-    {
-        bool b = false;
-        try { decode(s,DecodeMode.STRICT); }
-        catch (DecodeException e) { b = true; }
-        assert(b,s);
-    }
-
-    // Assert that things that should work, do
-    auto s = "hello";
-    assert(decode(s,                DecodeMode.STRICT) is s);
-    assert(decode("a &gt; b",       DecodeMode.STRICT) == "a > b");
-    assert(decode("a &lt; b",       DecodeMode.STRICT) == "a < b");
-    assert(decode("don&apos;t",     DecodeMode.STRICT) == "don't");
-    assert(decode("&quot;hi&quot;", DecodeMode.STRICT) == "\"hi\"");
-    assert(decode("cat &amp; dog",  DecodeMode.STRICT) == "cat & dog");
-    assert(decode("&#42;",          DecodeMode.STRICT) == "*");
-    assert(decode("&#x2A;",         DecodeMode.STRICT) == "*");
-    assert(decode("cat & dog",      DecodeMode.LOOSE) == "cat & dog");
-    assert(decode("a &gt b",        DecodeMode.LOOSE) == "a &gt b");
-    assert(decode("&#;",            DecodeMode.LOOSE) == "&#;");
-    assert(decode("&#x;",           DecodeMode.LOOSE) == "&#x;");
-    assert(decode("&#2G;",          DecodeMode.LOOSE) == "&#2G;");
-    assert(decode("&#x2G;",         DecodeMode.LOOSE) == "&#x2G;");
-
-    // Assert that things that shouldn't work, don't
-    assertNot("cat & dog");
-    assertNot("a &gt b");
-    assertNot("&#;");
-    assertNot("&#x;");
-    assertNot("&#2G;");
-    assertNot("&#x2G;");
-}
-
-/*
- * Class representing an XML document.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- */
-class Document : Element
-{
-    /*
-     * Contains all text which occurs before the root element.
-     * Defaults to &lt;?xml version="1.0"?&gt;
-     */
-    string prolog = "<?xml version=\"1.0\"?>";
-    /*
-     * Contains all text which occurs after the root element.
-     * Defaults to the empty string
-     */
-    string epilog;
-
-    /*
-     * Constructs a Document by parsing XML text.
-     *
-     * This function creates a complete DOM (Document Object Model) tree.
-     *
-     * The input to this function MUST be valid XML.
-     * This is enforced by DocumentParser's in contract.
-     *
-     * Params:
-     *      s = the complete XML text.
-     */
-    this(string s)
-    in
-    {
-        assert(s.length != 0);
-    }
-    do
-    {
-        auto xml = new DocumentParser(s);
-        string tagString = xml.tag.tagString;
-
-        this(xml.tag);
-        prolog = s[0 .. tagString.ptr - s.ptr];
-        parse(xml);
-        epilog = *xml.s;
-    }
-
-    /*
-     * Constructs a Document from a Tag.
-     *
-     * Params:
-     *      tag = the start tag of the document.
-     */
-    this(const(Tag) tag)
-    {
-        super(tag);
-    }
-
-    const
-    {
-        /*
-         * Compares two Documents for equality
-         *
-         * Example:
-         * --------------
-         * Document d1,d2;
-         * if (d1 == d2) { }
-         * --------------
-         */
-        override bool opEquals(scope const Object o) const
-        {
-            const doc = toType!(const Document)(o);
-            return prolog == doc.prolog
-                && (cast(const) this).Element.opEquals(cast(const) doc)
-                && epilog == doc.epilog;
-        }
-
-        /*
-         * Compares two Documents
-         *
-         * You should rarely need to call this function. It exists so that
-         * Documents can be used as associative array keys.
-         *
-         * Example:
-         * --------------
-         * Document d1,d2;
-         * if (d1 < d2) { }
-         * --------------
-         */
-        override int opCmp(scope const Object o) scope const
-        {
-            const doc = toType!(const Document)(o);
-            if (prolog != doc.prolog)
-                return prolog < doc.prolog ? -1 : 1;
-            if (int cmp = this.Element.opCmp(doc))
-                return cmp;
-            if (epilog != doc.epilog)
-                return epilog < doc.epilog ? -1 : 1;
-            return 0;
-        }
-
-        /*
-         * Returns the hash of a Document
-         *
-         * You should rarely need to call this function. It exists so that
-         * Documents can be used as associative array keys.
-         */
-        override size_t toHash() scope const @trusted
-        {
-            return hash(prolog, hash(epilog, (cast() this).Element.toHash()));
-        }
-
-        /*
-         * Returns the string representation of a Document. (That is, the
-         * complete XML of a document).
-         */
-        override string toString() scope const @safe
-        {
-            return prolog ~ super.toString() ~ epilog;
-        }
-    }
-}
-
-@system unittest
-{
-    // https://issues.dlang.org/show_bug.cgi?id=14966
-    auto xml = `<?xml version="1.0" encoding="UTF-8"?><foo></foo>`;
-
-    auto a = new Document(xml);
-    auto b = new Document(xml);
-    assert(a == b);
-    assert(!(a < b));
-    int[Document] aa;
-    aa[a] = 1;
-    assert(aa[b] == 1);
-
-    b ~= new Element("b");
-    assert(a < b);
-    assert(b > a);
-}
-
-/*
- * Class representing an XML element.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- */
-class Element : Item
-{
-    Tag tag; // The start tag of the element
-    Item[] items; // The element's items
-    Text[] texts; // The element's text items
-    CData[] cdatas; // The element's CData items
-    Comment[] comments; // The element's comments
-    ProcessingInstruction[] pis; // The element's processing instructions
-    Element[] elements; // The element's child elements
-
-    /*
-     * Constructs an Element given a name and a string to be used as a Text
-     * interior.
-     *
-     * Params:
-     *      name = the name of the element.
-     *      interior = (optional) the string interior.
-     *
-     * Example:
-     * -------------------------------------------------------
-     * auto element = new Element("title","Serenity")
-     *     // constructs the element <title>Serenity</title>
-     * -------------------------------------------------------
-     */
-    this(string name, string interior=null) @safe pure
-    {
-        this(new Tag(name));
-        if (interior.length != 0) opOpAssign!("~")(new Text(interior));
-    }
-
-    /*
-     * Constructs an Element from a Tag.
-     *
-     * Params:
-     *      tag_ = the start or empty tag of the element.
-     */
-    this(const(Tag) tag_) @safe pure
-    {
-        this.tag = new Tag(tag_.name);
-        tag.type = TagType.EMPTY;
-        foreach (k,v;tag_.attr) tag.attr[k] = v;
-        tag.tagString = tag_.tagString;
-    }
-
-    /*
-     * Append a text item to the interior of this element
-     *
-     * Params:
-     *      item = the item you wish to append.
-     *
-     * Example:
-     * --------------
-     * Element element;
-     * element ~= new Text("hello");
-     * --------------
-     */
-    void opOpAssign(string op)(Text item) @safe pure
-        if (op == "~")
-    {
-        texts ~= item;
-        appendItem(item);
-    }
-
-    /*
-     * Append a CData item to the interior of this element
-     *
-     * Params:
-     *      item = the item you wish to append.
-     *
-     * Example:
-     * --------------
-     * Element element;
-     * element ~= new CData("hello");
-     * --------------
-     */
-    void opOpAssign(string op)(CData item) @safe pure
-        if (op == "~")
-    {
-        cdatas ~= item;
-        appendItem(item);
-    }
-
-    /*
-     * Append a comment to the interior of this element
-     *
-     * Params:
-     *      item = the item you wish to append.
-     *
-     * Example:
-     * --------------
-     * Element element;
-     * element ~= new Comment("hello");
-     * --------------
-     */
-    void opOpAssign(string op)(Comment item) @safe pure
-        if (op == "~")
-    {
-        comments ~= item;
-        appendItem(item);
-    }
-
-    /*
-     * Append a processing instruction to the interior of this element
-     *
-     * Params:
-     *      item = the item you wish to append.
-     *
-     * Example:
-     * --------------
-     * Element element;
-     * element ~= new ProcessingInstruction("hello");
-     * --------------
-     */
-    void opOpAssign(string op)(ProcessingInstruction item) @safe pure
-        if (op == "~")
-    {
-        pis ~= item;
-        appendItem(item);
-    }
-
-    /*
-     * Append a complete element to the interior of this element
-     *
-     * Params:
-     *      item = the item you wish to append.
-     *
-     * Example:
-     * --------------
-     * Element element;
-     * Element other = new Element("br");
-     * element ~= other;
-     *    // appends element representing <br />
-     * --------------
-     */
-    void opOpAssign(string op)(Element item) @safe pure
-        if (op == "~")
-    {
-        elements ~= item;
-        appendItem(item);
-    }
-
-    private void appendItem(Item item) @safe pure
-    {
-        items ~= item;
-        if (tag.type == TagType.EMPTY && !item.isEmptyXML)
-            tag.type = TagType.START;
-    }
-
-    private void parse(ElementParser xml)
-    {
-        xml.onText = (string s) { opOpAssign!("~")(new Text(s)); };
-        xml.onCData = (string s) { opOpAssign!("~")(new CData(s)); };
-        xml.onComment = (string s) { opOpAssign!("~")(new Comment(s)); };
-        xml.onPI = (string s) { opOpAssign!("~")(new ProcessingInstruction(s)); };
-
-        xml.onStartTag[null] = (ElementParser xml)
-        {
-            auto e = new Element(xml.tag);
-            e.parse(xml);
-            opOpAssign!("~")(e);
-        };
-
-        xml.parse();
-    }
-
-    /*
-     * Compares two Elements for equality
-     *
-     * Example:
-     * --------------
-     * Element e1,e2;
-     * if (e1 == e2) { }
-     * --------------
-     */
-    override bool opEquals(scope const Object o) const
-    {
-        const element = toType!(const Element)(o);
-        immutable len = items.length;
-        if (len != element.items.length) return false;
-        foreach (i; 0 .. len)
-        {
-            if (!items[i].opEquals(element.items[i])) return false;
-        }
-        return true;
-    }
-
-    /*
-     * Compares two Elements
-     *
-     * You should rarely need to call this function. It exists so that Elements
-     * can be used as associative array keys.
-     *
-     * Example:
-     * --------------
-     * Element e1,e2;
-     * if (e1 < e2) { }
-     * --------------
-     */
-    override int opCmp(scope const Object o) @safe const
-    {
-        const element = toType!(const Element)(o);
-        for (uint i=0; ; ++i)
-        {
-            if (i == items.length && i == element.items.length) return 0;
-            if (i == items.length) return -1;
-            if (i == element.items.length) return 1;
-            if (!items[i].opEquals(element.items[i]))
-                return items[i].opCmp(element.items[i]);
-        }
-    }
-
-    /*
-     * Returns the hash of an Element
-     *
-     * You should rarely need to call this function. It exists so that Elements
-     * can be used as associative array keys.
-     */
-    override size_t toHash() scope const @safe
-    {
-        size_t hash = tag.toHash();
-        foreach (item;items) hash += item.toHash();
-        return hash;
-    }
-
-    const
-    {
-        /*
-         * Returns the decoded interior of an element.
-         *
-         * The element is assumed to contain text <i>only</i>. So, for
-         * example, given XML such as "&lt;title&gt;Good &amp;amp;
-         * Bad&lt;/title&gt;", will return "Good &amp; Bad".
-         *
-         * Params:
-         *      mode = (optional) Mode to use for decoding. (Defaults to LOOSE).
-         *
-         * Throws: DecodeException if decode fails
-         */
-        string text(DecodeMode mode=DecodeMode.LOOSE)
-        {
-            string buffer;
-            foreach (item;items)
-            {
-                Text t = cast(Text) item;
-                if (t is null) throw new DecodeException(item.toString());
-                buffer ~= decode(t.toString(),mode);
-            }
-            return buffer;
-        }
-
-        /*
-         * Returns an indented string representation of this item
-         *
-         * Params:
-         *      indent = (optional) number of spaces by which to indent this
-         *          element. Defaults to 2.
-         */
-        override string[] pretty(uint indent=2) scope
-        {
-            import std.algorithm.searching : count;
-            import std.string : rightJustify;
-
-            if (isEmptyXML) return [ tag.toEmptyString() ];
-
-            if (items.length == 1)
-            {
-                auto t = cast(const(Text))(items[0]);
-                if (t !is null)
-                {
-                    return [tag.toStartString() ~ t.toString() ~ tag.toEndString()];
-                }
-            }
-
-            string[] a = [ tag.toStartString() ];
-            foreach (item;items)
-            {
-                string[] b = item.pretty(indent);
-                foreach (s;b)
-                {
-                    a ~= rightJustify(s,count(s) + indent);
-                }
-            }
-            a ~= tag.toEndString();
-            return a;
-        }
-
-        /*
-         * Returns the string representation of an Element
-         *
-         * Example:
-         * --------------
-         * auto element = new Element("br");
-         * writefln(element.toString()); // writes "<br />"
-         * --------------
-         */
-        override string toString() scope @safe
-        {
-            if (isEmptyXML) return tag.toEmptyString();
-
-            string buffer = tag.toStartString();
-            foreach (item;items) { buffer ~= item.toString(); }
-            buffer ~= tag.toEndString();
-            return buffer;
-        }
-
-        override @property @safe pure @nogc nothrow bool isEmptyXML() const scope { return items.length == 0; }
-    }
-}
-
-/*
- * Tag types.
- *
- * $(DDOC_ENUM_MEMBERS START) Used for start tags
- * $(DDOC_ENUM_MEMBERS END) Used for end tags
- * $(DDOC_ENUM_MEMBERS EMPTY) Used for empty tags
- *
- */
-enum TagType { START, END, EMPTY }
-
-/*
- * Class representing an XML tag.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * The class invariant guarantees
- * <ul>
- * <li> that $(B type) is a valid enum TagType value</li>
- * <li> that $(B name) consists of valid characters</li>
- * <li> that each attribute name consists of valid characters</li>
- * </ul>
- */
-class Tag
-{
-    TagType type = TagType.START;   // Type of tag
-    string name;                    // Tag name
-    string[string] attr;            // Associative array of attributes
-    private string tagString;
-
-    invariant()
-    {
-        string s;
-        string t;
-
-        assert(type == TagType.START
-            || type == TagType.END
-            || type == TagType.EMPTY);
-
-        s = name;
-        try { checkName(s,t); }
-        catch (Err e) { assert(false,"Invalid tag name:" ~ e.toString()); }
-
-        foreach (k,v;attr)
-        {
-            s = k;
-            try { checkName(s,t); }
-            catch (Err e)
-                { assert(false,"Invalid attribute name:" ~ e.toString()); }
-        }
-    }
-
-    /*
-     * Constructs an instance of Tag with a specified name and type
-     *
-     * The constructor does not initialize the attributes. To initialize the
-     * attributes, you access the $(B attr) member variable.
-     *
-     * Params:
-     *      name = the Tag's name
-     *      type = (optional) the Tag's type. If omitted, defaults to
-     *          TagType.START.
-     *
-     * Example:
-     * --------------
-     * auto tag = new Tag("img",Tag.EMPTY);
-     * tag.attr["src"] = "http://example.com/example.jpg";
-     * --------------
-     */
-    this(string name, TagType type=TagType.START) @safe pure
-    {
-        this.name = name;
-        this.type = type;
-    }
-
-    /* Private constructor (so don't ddoc this!)
-     *
-     * Constructs a Tag by parsing the string representation, e.g. "<html>".
-     *
-     * The string is passed by reference, and is advanced over all characters
-     * consumed.
-     *
-     * The second parameter is a dummy parameter only, required solely to
-     * distinguish this constructor from the public one.
-     */
-    private this(ref string s, bool dummy) @safe pure
-    {
-        import std.algorithm.searching : countUntil;
-        import std.ascii : isWhite;
-        import std.utf : byCodeUnit;
-
-        tagString = s;
-        try
-        {
-            reqc(s,'<');
-            if (optc(s,'/')) type = TagType.END;
-            ptrdiff_t i = s.byCodeUnit.countUntil(">", "/>", " ", "\t", "\v", "\r", "\n", "\f");
-            name = s[0 .. i];
-            s = s[i .. $];
-
-            i = s.byCodeUnit.countUntil!(a => !isWhite(a));
-            s = s[i .. $];
-
-            while (s.length > 0 && s[0] != '>' && s[0] != '/')
-            {
-                i = s.byCodeUnit.countUntil("=", " ", "\t", "\v", "\r", "\n", "\f");
-                string key = s[0 .. i];
-                s = s[i .. $];
-
-                i = s.byCodeUnit.countUntil!(a => !isWhite(a));
-                s = s[i .. $];
-                reqc(s,'=');
-                i = s.byCodeUnit.countUntil!(a => !isWhite(a));
-                s = s[i .. $];
-
-                immutable char quote = requireOneOf(s,"'\"");
-                i = s.byCodeUnit.countUntil(quote);
-                string val = decode(s[0 .. i], DecodeMode.LOOSE);
-                s = s[i .. $];
-                reqc(s,quote);
-
-                i = s.byCodeUnit.countUntil!(a => !isWhite(a));
-                s = s[i .. $];
-                attr[key] = val;
-            }
-            if (optc(s,'/'))
-            {
-                if (type == TagType.END) throw new TagException("");
-                type = TagType.EMPTY;
-            }
-            reqc(s,'>');
-            tagString.length = tagString.length - s.length;
-        }
-        catch (XMLException e)
-        {
-            tagString.length = tagString.length - s.length;
-            throw new TagException(tagString);
-        }
-    }
-
-    const
-    {
-        /*
-         * Compares two Tags for equality
-         *
-         * You should rarely need to call this function. It exists so that Tags
-         * can be used as associative array keys.
-         *
-         * Example:
-         * --------------
-         * Tag tag1,tag2
-         * if (tag1 == tag2) { }
-         * --------------
-         */
-        override bool opEquals(scope Object o)
-        {
-            const tag = toType!(const Tag)(o);
-            return
-                (name != tag.name) ? false : (
-                (attr != tag.attr) ? false : (
-                (type != tag.type) ? false : (
-            true )));
-        }
-
-        /*
-         * Compares two Tags
-         *
-         * Example:
-         * --------------
-         * Tag tag1,tag2
-         * if (tag1 < tag2) { }
-         * --------------
-         */
-        override int opCmp(Object o)
-        {
-            const tag = toType!(const Tag)(o);
-            // Note that attr is an AA, so the comparison is nonsensical (bug 10381)
-            return
-                ((name != tag.name) ? ( name < tag.name ? -1 : 1 ) :
-                ((attr != tag.attr) ? ( cast(void *) attr < cast(void*) tag.attr ? -1 : 1 ) :
-                ((type != tag.type) ? ( type < tag.type ? -1 : 1 ) :
-            0 )));
-        }
-
-        /*
-         * Returns the hash of a Tag
-         *
-         * You should rarely need to call this function. It exists so that Tags
-         * can be used as associative array keys.
-         */
-        override size_t toHash()
-        {
-            return .hashOf(name);
-        }
-
-        /*
-         * Returns the string representation of a Tag
-         *
-         * Example:
-         * --------------
-         * auto tag = new Tag("book",TagType.START);
-         * writefln(tag.toString()); // writes "<book>"
-         * --------------
-         */
-        override string toString() @safe
-        {
-            if (isEmpty) return toEmptyString();
-            return (isEnd) ? toEndString() : toStartString();
-        }
-
-        private
-        {
-            string toNonEndString() @safe
-            {
-                import std.format : format;
-
-                string s = "<" ~ name;
-                foreach (key,val;attr)
-                    s ~= format(" %s=\"%s\"",key,encode(val));
-                return s;
-            }
-
-            string toStartString() @safe { return toNonEndString() ~ ">"; }
-
-            string toEndString() @safe { return "</" ~ name ~ ">"; }
-
-            string toEmptyString() @safe { return toNonEndString() ~ " />"; }
-        }
-
-        /*
-         * Returns true if the Tag is a start tag
-         *
-         * Example:
-         * --------------
-         * if (tag.isStart) { }
-         * --------------
-         */
-        @property bool isStart() @safe @nogc pure nothrow { return type == TagType.START; }
-
-        /*
-         * Returns true if the Tag is an end tag
-         *
-         * Example:
-         * --------------
-         * if (tag.isEnd) { }
-         * --------------
-         */
-        @property bool isEnd() @safe @nogc pure nothrow { return type == TagType.END;   }
-
-        /*
-         * Returns true if the Tag is an empty tag
-         *
-         * Example:
-         * --------------
-         * if (tag.isEmpty) { }
-         * --------------
-         */
-        @property bool isEmpty() @safe @nogc pure nothrow { return type == TagType.EMPTY; }
-    }
-}
-
-/*
- * Class representing a comment
- */
-class Comment : Item
-{
-    private string content;
-
-    /*
-     * Construct a comment
-     *
-     * Params:
-     *      content = the body of the comment
-     *
-     * Throws: CommentException if the comment body is illegal (contains "--"
-     * or exactly equals "-")
-     *
-     * Example:
-     * --------------
-     * auto item = new Comment("This is a comment");
-     *    // constructs <!--This is a comment-->
-     * --------------
-     */
-    this(string content) @safe pure
-    {
-        import std.string : indexOf;
-
-        if (content == "-" || content.indexOf("--") != -1)
-            throw new CommentException(content);
-        this.content = content;
-    }
-
-    /*
-     * Compares two comments for equality
-     *
-     * Example:
-     * --------------
-     * Comment item1,item2;
-     * if (item1 == item2) { }
-     * --------------
-     */
-    override bool opEquals(scope const Object o) const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const Comment) item;
-        return t !is null && content == t.content;
-    }
-
-    /*
-     * Compares two comments
-     *
-     * You should rarely need to call this function. It exists so that Comments
-     * can be used as associative array keys.
-     *
-     * Example:
-     * --------------
-     * Comment item1,item2;
-     * if (item1 < item2) { }
-     * --------------
-     */
-    override int opCmp(scope const Object o) scope const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const Comment) item;
-        return t !is null && (content != t.content
-            ? (content < t.content ? -1 : 1 ) : 0 );
-    }
-
-    /*
-     * Returns the hash of a Comment
-     *
-     * You should rarely need to call this function. It exists so that Comments
-     * can be used as associative array keys.
-     */
-    override size_t toHash() scope const nothrow { return hash(content); }
-
-    /*
-     * Returns a string representation of this comment
-     */
-    override string toString() scope const @safe pure nothrow { return "<!--" ~ content ~ "-->"; }
-
-    override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=16241
-@safe unittest
-{
-    import std.exception : assertThrown;
-    auto c = new Comment("==");
-    assert(c.content == "==");
-    assertThrown!CommentException(new Comment("--"));
-}
-
-/*
- * Class representing a Character Data section
- */
-class CData : Item
-{
-    private string content;
-
-    /*
-     * Construct a character data section
-     *
-     * Params:
-     *      content = the body of the character data segment
-     *
-     * Throws: CDataException if the segment body is illegal (contains "]]>")
-     *
-     * Example:
-     * --------------
-     * auto item = new CData("<b>hello</b>");
-     *    // constructs <![CDATA[<b>hello</b>]]>
-     * --------------
-     */
-    this(string content) @safe pure
-    {
-        import std.string : indexOf;
-        if (content.indexOf("]]>") != -1) throw new CDataException(content);
-        this.content = content;
-    }
-
-    /*
-     * Compares two CDatas for equality
-     *
-     * Example:
-     * --------------
-     * CData item1,item2;
-     * if (item1 == item2) { }
-     * --------------
-     */
-    override bool opEquals(scope const Object o) const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const CData) item;
-        return t !is null && content == t.content;
-    }
-
-    /*
-     * Compares two CDatas
-     *
-     * You should rarely need to call this function. It exists so that CDatas
-     * can be used as associative array keys.
-     *
-     * Example:
-     * --------------
-     * CData item1,item2;
-     * if (item1 < item2) { }
-     * --------------
-     */
-    override int opCmp(scope const Object o) scope const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const CData) item;
-        return t !is null && (content != t.content
-            ? (content < t.content ? -1 : 1 ) : 0 );
-    }
-
-    /*
-     * Returns the hash of a CData
-     *
-     * You should rarely need to call this function. It exists so that CDatas
-     * can be used as associative array keys.
-     */
-    override size_t toHash() scope const nothrow { return hash(content); }
-
-    /*
-     * Returns a string representation of this CData section
-     */
-    override string toString() scope const @safe pure nothrow { return cdata ~ content ~ "]]>"; }
-
-    override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
-}
-
-/*
- * Class representing a text (aka Parsed Character Data) section
- */
-class Text : Item
-{
-    private string content;
-
-    /*
-     * Construct a text (aka PCData) section
-     *
-     * Params:
-     *      content = the text. This function encodes the text before
-     *      insertion, so it is safe to insert any text
-     *
-     * Example:
-     * --------------
-     * auto Text = new CData("a < b");
-     *    // constructs a &lt; b
-     * --------------
-     */
-    this(string content) @safe pure
-    {
-        this.content = encode(content);
-    }
-
-    /*
-     * Compares two text sections for equality
-     *
-     * Example:
-     * --------------
-     * Text item1,item2;
-     * if (item1 == item2) { }
-     * --------------
-     */
-    override bool opEquals(scope const Object o) const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const Text) item;
-        return t !is null && content == t.content;
-    }
-
-    /*
-     * Compares two text sections
-     *
-     * You should rarely need to call this function. It exists so that Texts
-     * can be used as associative array keys.
-     *
-     * Example:
-     * --------------
-     * Text item1,item2;
-     * if (item1 < item2) { }
-     * --------------
-     */
-    override int opCmp(scope const Object o) scope const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const Text) item;
-        return t !is null
-            && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
-    }
-
-    /*
-     * Returns the hash of a text section
-     *
-     * You should rarely need to call this function. It exists so that Texts
-     * can be used as associative array keys.
-     */
-    override size_t toHash() scope const nothrow { return hash(content); }
-
-    /*
-     * Returns a string representation of this Text section
-     */
-    override string toString() scope const @safe @nogc pure nothrow { return content; }
-
-    /*
-     * Returns true if the content is the empty string
-     */
-    override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return content.length == 0; }
-}
-
-/*
- * Class representing an XML Instruction section
- */
-class XMLInstruction : Item
-{
-    private string content;
-
-    /*
-     * Construct an XML Instruction section
-     *
-     * Params:
-     *      content = the body of the instruction segment
-     *
-     * Throws: XIException if the segment body is illegal (contains ">")
-     *
-     * Example:
-     * --------------
-     * auto item = new XMLInstruction("ATTLIST");
-     *    // constructs <!ATTLIST>
-     * --------------
-     */
-    this(string content) @safe pure
-    {
-        import std.string : indexOf;
-        if (content.indexOf(">") != -1) throw new XIException(content);
-        this.content = content;
-    }
-
-    /*
-     * Compares two XML instructions for equality
-     *
-     * Example:
-     * --------------
-     * XMLInstruction item1,item2;
-     * if (item1 == item2) { }
-     * --------------
-     */
-    override bool opEquals(scope const Object o) const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const XMLInstruction) item;
-        return t !is null && content == t.content;
-    }
-
-    /*
-     * Compares two XML instructions
-     *
-     * You should rarely need to call this function. It exists so that
-     * XmlInstructions can be used as associative array keys.
-     *
-     * Example:
-     * --------------
-     * XMLInstruction item1,item2;
-     * if (item1 < item2) { }
-     * --------------
-     */
-    override int opCmp(scope const Object o) scope const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const XMLInstruction) item;
-        return t !is null
-            && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
-    }
-
-    /*
-     * Returns the hash of an XMLInstruction
-     *
-     * You should rarely need to call this function. It exists so that
-     * XmlInstructions can be used as associative array keys.
-     */
-    override size_t toHash() scope const nothrow { return hash(content); }
-
-    /*
-     * Returns a string representation of this XmlInstruction
-     */
-    override string toString() scope const @safe pure nothrow { return "<!" ~ content ~ ">"; }
-
-    override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
-}
-
-/*
- * Class representing a Processing Instruction section
- */
-class ProcessingInstruction : Item
-{
-    private string content;
-
-    /*
-     * Construct a Processing Instruction section
-     *
-     * Params:
-     *      content = the body of the instruction segment
-     *
-     * Throws: PIException if the segment body is illegal (contains "?>")
-     *
-     * Example:
-     * --------------
-     * auto item = new ProcessingInstruction("php");
-     *    // constructs <?php?>
-     * --------------
-     */
-    this(string content) @safe pure
-    {
-        import std.string : indexOf;
-        if (content.indexOf("?>") != -1) throw new PIException(content);
-        this.content = content;
-    }
-
-    /*
-     * Compares two processing instructions for equality
-     *
-     * Example:
-     * --------------
-     * ProcessingInstruction item1,item2;
-     * if (item1 == item2) { }
-     * --------------
-     */
-    override bool opEquals(scope const Object o) const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const ProcessingInstruction) item;
-        return t !is null && content == t.content;
-    }
-
-    /*
-     * Compares two processing instructions
-     *
-     * You should rarely need to call this function. It exists so that
-     * ProcessingInstructions can be used as associative array keys.
-     *
-     * Example:
-     * --------------
-     * ProcessingInstruction item1,item2;
-     * if (item1 < item2) { }
-     * --------------
-     */
-    override int opCmp(scope const Object o) scope const
-    {
-        const item = toType!(const Item)(o);
-        const t = cast(const ProcessingInstruction) item;
-        return t !is null
-            && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
-    }
-
-    /*
-     * Returns the hash of a ProcessingInstruction
-     *
-     * You should rarely need to call this function. It exists so that
-     * ProcessingInstructions can be used as associative array keys.
-     */
-    override size_t toHash() scope const nothrow { return hash(content); }
-
-    /*
-     * Returns a string representation of this ProcessingInstruction
-     */
-    override string toString() scope const @safe pure nothrow { return "<?" ~ content ~ "?>"; }
-
-    override @property @safe @nogc pure nothrow bool isEmptyXML() scope const { return false; } // Returns false always
-}
-
-/*
- * Abstract base class for XML items
- */
-abstract class Item
-{
-    // Compares with another Item of same type for equality
-    abstract override bool opEquals(scope const Object o) @safe const;
-
-    // Compares with another Item of same type
-    abstract override int opCmp(scope const Object o) @safe const;
-
-    // Returns the hash of this item
-    abstract override size_t toHash() @safe scope const;
-
-    // Returns a string representation of this item
-    abstract override string toString() @safe scope const;
-
-    /*
-     * Returns an indented string representation of this item
-     *
-     * Params:
-     *      indent = number of spaces by which to indent child elements
-     */
-    string[] pretty(uint indent) @safe scope const
-    {
-        import std.string : strip;
-        string s = strip(toString());
-        return s.length == 0 ? [] : [ s ];
-    }
-
-    // Returns true if the item represents empty XML text
-    abstract @property @safe @nogc pure nothrow bool isEmptyXML() scope const;
-}
-
-/*
- * Class for parsing an XML Document.
- *
- * This is a subclass of ElementParser. Most of the useful functions are
- * documented there.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Bugs:
- *      Currently only supports UTF documents.
- *
- *      If there is an encoding attribute in the prolog, it is ignored.
- *
- */
-class DocumentParser : ElementParser
-{
-    string xmlText;
-
-    /*
-     * Constructs a DocumentParser.
-     *
-     * The input to this function MUST be valid XML.
-     * This is enforced by the function's in contract.
-     *
-     * Params:
-     *      xmlText_ = the entire XML document as text
-     *
-     */
-    this(string xmlText_)
-    in
-    {
-        assert(xmlText_.length != 0);
-        try
-        {
-            // Confirm that the input is valid XML
-            check(xmlText_);
-        }
-        catch (CheckException e)
-        {
-            // And if it's not, tell the user why not
-            assert(false, "\n" ~ e.toString());
-        }
-    }
-    do
-    {
-        xmlText = xmlText_;
-        s = &xmlText;
-        super();    // Initialize everything
-        parse();    // Parse through the root tag (but not beyond)
-    }
-}
-
-@system unittest
-{
-    auto doc = new Document("<root><child><grandchild/></child></root>");
-    assert(doc.elements.length == 1);
-    assert(doc.elements[0].tag.name == "child");
-    assert(doc.items == doc.elements);
-}
-
-/*
- * Class for parsing an XML element.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Note that you cannot construct instances of this class directly. You can
- * construct a DocumentParser (which is a subclass of ElementParser), but
- * otherwise, Instances of ElementParser will be created for you by the
- * library, and passed your way via onStartTag handlers.
- *
- */
-class ElementParser
-{
-    alias Handler = void delegate(string);
-    alias ElementHandler = void delegate(in Element element);
-    alias ParserHandler = void delegate(ElementParser parser);
-
-    private
-    {
-        Tag tag_;
-        string elementStart;
-        string* s;
-
-        Handler commentHandler = null;
-        Handler cdataHandler = null;
-        Handler xiHandler = null;
-        Handler piHandler = null;
-        Handler rawTextHandler = null;
-        Handler textHandler = null;
-
-        // Private constructor for start tags
-        this(ElementParser parent) @safe @nogc pure nothrow
-        {
-            s = parent.s;
-            this();
-            tag_ = parent.tag_;
-        }
-
-        // Private constructor for empty tags
-        this(Tag tag, string* t) @safe @nogc pure nothrow
-        {
-            s = t;
-            this();
-            tag_ = tag;
-        }
-    }
-
-    /*
-     * The Tag at the start of the element being parsed. You can read this to
-     * determine the tag's name and attributes.
-     */
-    @property @safe @nogc pure nothrow const(Tag) tag() const { return tag_; }
-
-    /*
-     * Register a handler which will be called whenever a start tag is
-     * encountered which matches the specified name. You can also pass null as
-     * the name, in which case the handler will be called for any unmatched
-     * start tag.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever a <podcast> start tag is encountered
-     * onStartTag["podcast"] = (ElementParser xml)
-     * {
-     *     // Your code here
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     *
-     * // call myEpisodeStartHandler (defined elsewhere) whenever an <episode>
-     * // start tag is encountered
-     * onStartTag["episode"] = &myEpisodeStartHandler;
-     *
-     * // call delegate dg for all other start tags
-     * onStartTag[null] = dg;
-     * --------------
-     *
-     * This library will supply your function with a new instance of
-     * ElementHandler, which may be used to parse inside the element whose
-     * start tag was just found, or to identify the tag attributes of the
-     * element, etc.
-     *
-     * Note that your function will be called for both start tags and empty
-     * tags. That is, we make no distinction between &lt;br&gt;&lt;/br&gt;
-     * and &lt;br/&gt;.
-     */
-    ParserHandler[string] onStartTag;
-
-    /*
-     * Register a handler which will be called whenever an end tag is
-     * encountered which matches the specified name. You can also pass null as
-     * the name, in which case the handler will be called for any unmatched
-     * end tag.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever a </podcast> end tag is encountered
-     * onEndTag["podcast"] = (in Element e)
-     * {
-     *     // Your code here
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     *
-     * // call myEpisodeEndHandler (defined elsewhere) whenever an </episode>
-     * // end tag is encountered
-     * onEndTag["episode"] = &myEpisodeEndHandler;
-     *
-     * // call delegate dg for all other end tags
-     * onEndTag[null] = dg;
-     * --------------
-     *
-     * Note that your function will be called for both start tags and empty
-     * tags. That is, we make no distinction between &lt;br&gt;&lt;/br&gt;
-     * and &lt;br/&gt;.
-     */
-    ElementHandler[string] onEndTag;
-
-    protected this() @safe @nogc pure nothrow
-    {
-        elementStart = *s;
-    }
-
-    /*
-     * Register a handler which will be called whenever text is encountered.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever text is encountered
-     * onText = (string s)
-     * {
-     *     // Your code here
-     *
-     *     // The passed parameter s will have been decoded by the time you see
-     *     // it, and so may contain any character.
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     * --------------
-     */
-    @property @safe @nogc pure nothrow void onText(Handler handler) { textHandler = handler; }
-
-    /*
-     * Register an alternative handler which will be called whenever text
-     * is encountered. This differs from onText in that onText will decode
-     * the text, whereas onTextRaw will not. This allows you to make design
-     * choices, since onText will be more accurate, but slower, while
-     * onTextRaw will be faster, but less accurate. Of course, you can
-     * still call decode() within your handler, if you want, but you'd
-     * probably want to use onTextRaw only in circumstances where you
-     * know that decoding is unnecessary.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever text is encountered
-     * onText = (string s)
-     * {
-     *     // Your code here
-     *
-     *     // The passed parameter s will NOT have been decoded.
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     * --------------
-     */
-    @safe @nogc pure nothrow void onTextRaw(Handler handler) { rawTextHandler = handler; }
-
-    /*
-     * Register a handler which will be called whenever a character data
-     * segment is encountered.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever a CData section is encountered
-     * onCData = (string s)
-     * {
-     *     // Your code here
-     *
-     *     // The passed parameter s does not include the opening <![CDATA[
-     *     // nor closing ]]>
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     * --------------
-     */
-    @property @safe @nogc pure nothrow void onCData(Handler handler) { cdataHandler = handler; }
-
-    /*
-     * Register a handler which will be called whenever a comment is
-     * encountered.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever a comment is encountered
-     * onComment = (string s)
-     * {
-     *     // Your code here
-     *
-     *     // The passed parameter s does not include the opening <!-- nor
-     *     // closing -->
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     * --------------
-     */
-    @property @safe @nogc pure nothrow void onComment(Handler handler) { commentHandler = handler; }
-
-    /*
-     * Register a handler which will be called whenever a processing
-     * instruction is encountered.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever a processing instruction is encountered
-     * onPI = (string s)
-     * {
-     *     // Your code here
-     *
-     *     // The passed parameter s does not include the opening <? nor
-     *     // closing ?>
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     * --------------
-     */
-    @property @safe @nogc pure nothrow void onPI(Handler handler) { piHandler = handler; }
-
-    /*
-     * Register a handler which will be called whenever an XML instruction is
-     * encountered.
-     *
-     * Example:
-     * --------------
-     * // Call this function whenever an XML instruction is encountered
-     * // (Note: XML instructions may only occur preceding the root tag of a
-     * // document).
-     * onPI = (string s)
-     * {
-     *     // Your code here
-     *
-     *     // The passed parameter s does not include the opening <! nor
-     *     // closing >
-     *     //
-     *     // This is a a closure, so code here may reference
-     *     // variables which are outside of this scope
-     * };
-     * --------------
-     */
-    @property @safe @nogc pure nothrow void onXI(Handler handler) { xiHandler = handler; }
-
-    /*
-     * Parse an XML element.
-     *
-     * Parsing will continue until the end of the current element. Any items
-     * encountered for which a handler has been registered will invoke that
-     * handler.
-     *
-     * Throws: various kinds of XMLException
-     */
-    void parse()
-    {
-        import std.algorithm.searching : startsWith;
-        import std.string : indexOf;
-
-        string t;
-        const Tag root = tag_;
-        Tag[string] startTags;
-        if (tag_ !is null) startTags[tag_.name] = tag_;
-
-        while (s.length != 0)
-        {
-            if (startsWith(*s,"<!--"))
-            {
-                chop(*s,4);
-                t = chop(*s,indexOf(*s,"-->"));
-                if (commentHandler.funcptr !is null) commentHandler(t);
-                chop(*s,3);
-            }
-            else if (startsWith(*s,"<![CDATA["))
-            {
-                chop(*s,9);
-                t = chop(*s,indexOf(*s,"]]>"));
-                if (cdataHandler.funcptr !is null) cdataHandler(t);
-                chop(*s,3);
-            }
-            else if (startsWith(*s,"<!"))
-            {
-                chop(*s,2);
-                t = chop(*s,indexOf(*s,">"));
-                if (xiHandler.funcptr !is null) xiHandler(t);
-                chop(*s,1);
-            }
-            else if (startsWith(*s,"<?"))
-            {
-                chop(*s,2);
-                t = chop(*s,indexOf(*s,"?>"));
-                if (piHandler.funcptr !is null) piHandler(t);
-                chop(*s,2);
-            }
-            else if (startsWith(*s,"<"))
-            {
-                tag_ = new Tag(*s,true);
-                if (root is null)
-                    return; // Return to constructor of derived class
-
-                if (tag_.isStart)
-                {
-                    startTags[tag_.name] = tag_;
-
-                    auto parser = new ElementParser(this);
-
-                    auto handler = tag_.name in onStartTag;
-                    if (handler !is null) (*handler)(parser);
-                    else
-                    {
-                        handler = null in onStartTag;
-                        if (handler !is null) (*handler)(parser);
-                    }
-                }
-                else if (tag_.isEnd)
-                {
-                    const startTag = startTags[tag_.name];
-                    string text;
-
-                    if (startTag.tagString.length == 0)
-                        assert(0);
-
-                    immutable(char)* p = startTag.tagString.ptr
-                        + startTag.tagString.length;
-                    immutable(char)* q = &tag_.tagString[0];
-                    text = decode(p[0..(q-p)], DecodeMode.LOOSE);
-
-                    auto element = new Element(startTag);
-                    if (text.length != 0) element ~= new Text(text);
-
-                    auto handler = tag_.name in onEndTag;
-                    if (handler !is null) (*handler)(element);
-                    else
-                    {
-                        handler = null in onEndTag;
-                        if (handler !is null) (*handler)(element);
-                    }
-
-                    if (tag_.name == root.name) return;
-                }
-                else if (tag_.isEmpty)
-                {
-                    Tag startTag = new Tag(tag_.name);
-
-                    // FIX by hed010gy
-                    // https://issues.dlang.org/show_bug.cgi?id=2979
-                    if (tag_.attr.length > 0)
-                          foreach (tn,tv; tag_.attr) startTag.attr[tn]=tv;
-                    // END FIX
-
-                    // Handle the pretend start tag
-                    string s2;
-                    auto parser = new ElementParser(startTag,&s2);
-                    auto handler1 = startTag.name in onStartTag;
-                    if (handler1 !is null) (*handler1)(parser);
-                    else
-                    {
-                        handler1 = null in onStartTag;
-                        if (handler1 !is null) (*handler1)(parser);
-                    }
-
-                    // Handle the pretend end tag
-                    auto element = new Element(startTag);
-                    auto handler2 = tag_.name in onEndTag;
-                    if (handler2 !is null) (*handler2)(element);
-                    else
-                    {
-                        handler2 = null in onEndTag;
-                        if (handler2 !is null) (*handler2)(element);
-                    }
-                }
-            }
-            else
-            {
-                t = chop(*s,indexOf(*s,"<"));
-                if (rawTextHandler.funcptr !is null)
-                    rawTextHandler(t);
-                else if (textHandler.funcptr !is null)
-                    textHandler(decode(t,DecodeMode.LOOSE));
-            }
-        }
-    }
-
-    /*
-     * Returns that part of the element which has already been parsed
-     */
-    override string toString() const @nogc @safe pure nothrow
-    {
-        assert(elementStart.length >= s.length);
-        return elementStart[0 .. elementStart.length - s.length];
-    }
-
-}
-
-private
-{
-    template Check(string msg)
-    {
-        string old = s;
-
-        void fail() @safe pure
-        {
-            s = old;
-            throw new Err(s,msg);
-        }
-
-        void fail(Err e) @safe pure
-        {
-            s = old;
-            throw new Err(s,msg,e);
-        }
-
-        void fail(string msg2) @safe pure
-        {
-            fail(new Err(s,msg2));
-        }
-    }
-
-    void checkMisc(ref string s) @safe pure // rule 27
-    {
-        import std.algorithm.searching : startsWith;
-
-        mixin Check!("Misc");
-
-        try
-        {
-                 if (s.startsWith("<!--")) { checkComment(s); }
-            else if (s.startsWith("<?"))   { checkPI(s); }
-            else                           { checkSpace(s); }
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkDocument(ref string s) @safe pure // rule 1
-    {
-        mixin Check!("Document");
-        try
-        {
-            checkProlog(s);
-            checkElement(s);
-            star!(checkMisc)(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkChars(ref string s) @safe pure // rule 2
-    {
-        // TO DO - Fix std.utf stride and decode functions, then use those
-        // instead
-        import std.format : format;
-
-        mixin Check!("Chars");
-
-        dchar c;
-        ptrdiff_t n = -1;
-        // 'i' must not be smaller than size_t because size_t is used internally in
-        // aApply.d and it will be cast e.g to (int *) which fails on BigEndian targets.
-        foreach (size_t i, dchar d; s)
-        {
-            if (!isChar(d))
-            {
-                c = d;
-                n = i;
-                break;
-            }
-        }
-        if (n != -1)
-        {
-            s = s[n..$];
-            fail(format("invalid character: U+%04X",c));
-        }
-    }
-
-    void checkSpace(ref string s) @safe pure // rule 3
-    {
-        import std.algorithm.searching : countUntil;
-        import std.ascii : isWhite;
-        import std.utf : byCodeUnit;
-
-        mixin Check!("Whitespace");
-        ptrdiff_t i = s.byCodeUnit.countUntil!(a => !isWhite(a));
-        if (i == -1 && s.length > 0 && isWhite(s[0]))
-            s = s[$ .. $];
-        else if (i > -1)
-            s = s[i .. $];
-        if (s is old) fail();
-    }
-
-    void checkName(ref string s, out string name) @safe pure // rule 5
-    {
-        mixin Check!("Name");
-
-        if (s.length == 0) fail();
-        ptrdiff_t n;
-        // 'i' must not be smaller than size_t because size_t is used internally in
-        // aApply.d and it will be cast e.g to (int *) which fails on BigEndian targets.
-        foreach (size_t i, dchar c; s)
-        {
-            if (c == '_' || c == ':' || isLetter(c)) continue;
-            if (i == 0) fail();
-            if (c == '-' || c == '.' || isDigit(c)
-                || isCombiningChar(c) || isExtender(c)) continue;
-            n = i;
-            break;
-        }
-        name = s[0 .. n];
-        s = s[n..$];
-    }
-
-    void checkAttValue(ref string s) @safe pure // rule 10
-    {
-        import std.algorithm.searching : countUntil;
-        import std.utf : byCodeUnit;
-
-        mixin Check!("AttValue");
-
-        if (s.length == 0) fail();
-        char c = s[0];
-        if (c != '\u0022' && c != '\u0027')
-            fail("attribute value requires quotes");
-        s = s[1..$];
-        for (;;)
-        {
-            s = s[s.byCodeUnit.countUntil(c) .. $];
-            if (s.length == 0) fail("unterminated attribute value");
-            if (s[0] == '<') fail("< found in attribute value");
-            if (s[0] == c) break;
-            try { checkReference(s); } catch (Err e) { fail(e); }
-        }
-        s = s[1..$];
-    }
-
-    void checkCharData(ref string s) @safe pure // rule 14
-    {
-        import std.algorithm.searching : startsWith;
-
-        mixin Check!("CharData");
-
-        while (s.length != 0)
-        {
-            if (s.startsWith("&")) break;
-            if (s.startsWith("<")) break;
-            if (s.startsWith("]]>")) fail("]]> found within char data");
-            s = s[1..$];
-        }
-    }
-
-    void checkComment(ref string s) @safe pure // rule 15
-    {
-        import std.string : indexOf;
-
-        mixin Check!("Comment");
-
-        try { checkLiteral("<!--",s); } catch (Err e) { fail(e); }
-        ptrdiff_t n = s.indexOf("--");
-        if (n == -1) fail("unterminated comment");
-        s = s[n..$];
-        try { checkLiteral("-->",s); } catch (Err e) { fail(e); }
-    }
-
-    void checkPI(ref string s) @safe pure // rule 16
-    {
-        mixin Check!("PI");
-
-        try
-        {
-            checkLiteral("<?",s);
-            checkEnd("?>",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkCDSect(ref string s) @safe pure // rule 18
-    {
-        mixin Check!("CDSect");
-
-        try
-        {
-            checkLiteral(cdata,s);
-            checkEnd("]]>",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkProlog(ref string s) @safe pure // rule 22
-    {
-        mixin Check!("Prolog");
-
-        try
-        {
-            /* The XML declaration is optional
-             * http://www.w3.org/TR/2008/REC-xml-20081126/#NT-prolog
-             */
-            opt!(checkXMLDecl)(s);
-
-            star!(checkMisc)(s);
-            opt!(seq!(checkDocTypeDecl,star!(checkMisc)))(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkXMLDecl(ref string s) @safe pure // rule 23
-    {
-        mixin Check!("XMLDecl");
-
-        try
-        {
-            checkLiteral("<?xml",s);
-            checkVersionInfo(s);
-            opt!(checkEncodingDecl)(s);
-            opt!(checkSDDecl)(s);
-            opt!(checkSpace)(s);
-            checkLiteral("?>",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkVersionInfo(ref string s) @safe pure // rule 24
-    {
-        mixin Check!("VersionInfo");
-
-        try
-        {
-            checkSpace(s);
-            checkLiteral("version",s);
-            checkEq(s);
-            quoted!(checkVersionNum)(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkEq(ref string s) @safe pure // rule 25
-    {
-        mixin Check!("Eq");
-
-        try
-        {
-            opt!(checkSpace)(s);
-            checkLiteral("=",s);
-            opt!(checkSpace)(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkVersionNum(ref string s) @safe pure // rule 26
-    {
-        import std.algorithm.searching : countUntil;
-        import std.utf : byCodeUnit;
-
-        mixin Check!("VersionNum");
-
-        s = s[s.byCodeUnit.countUntil('\"') .. $];
-        if (s is old) fail();
-    }
-
-    void checkDocTypeDecl(ref string s) @safe pure // rule 28
-    {
-        mixin Check!("DocTypeDecl");
-
-        try
-        {
-            checkLiteral("<!DOCTYPE",s);
-            //
-            // TO DO -- ensure DOCTYPE is well formed
-            // (But not yet. That's one of our "future directions")
-            //
-            checkEnd(">",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkSDDecl(ref string s) @safe pure // rule 32
-    {
-        import std.algorithm.searching : startsWith;
-
-        mixin Check!("SDDecl");
-
-        try
-        {
-            checkSpace(s);
-            checkLiteral("standalone",s);
-            checkEq(s);
-        }
-        catch (Err e) { fail(e); }
-
-        int n = 0;
-             if (s.startsWith("'yes'") || s.startsWith("\"yes\"")) n = 5;
-        else if (s.startsWith("'no'" ) || s.startsWith("\"no\"" )) n = 4;
-        else fail("standalone attribute value must be 'yes', \"yes\","~
-            " 'no' or \"no\"");
-        s = s[n..$];
-    }
-
-    void checkElement(ref string s) @safe pure // rule 39
-    {
-        mixin Check!("Element");
-
-        string sname,ename,t;
-        try { checkTag(s,t,sname); } catch (Err e) { fail(e); }
-
-        if (t == "STag")
-        {
-            try
-            {
-                checkContent(s);
-                t = s;
-                checkETag(s,ename);
-            }
-            catch (Err e) { fail(e); }
-
-            if (sname != ename)
-            {
-                s = t;
-                fail("end tag name \"" ~ ename
-                    ~ "\" differs from start tag name \""~sname~"\"");
-            }
-        }
-    }
-
-    // rules 40 and 44
-    void checkTag(ref string s, out string type, out string name) @safe pure
-    {
-        mixin Check!("Tag");
-
-        try
-        {
-            type = "STag";
-            checkLiteral("<",s);
-            checkName(s,name);
-            star!(seq!(checkSpace,checkAttribute))(s);
-            opt!(checkSpace)(s);
-            if (s.length != 0 && s[0] == '/')
-            {
-                s = s[1..$];
-                type = "ETag";
-            }
-            checkLiteral(">",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkAttribute(ref string s) @safe pure // rule 41
-    {
-        mixin Check!("Attribute");
-
-        try
-        {
-            string name;
-            checkName(s,name);
-            checkEq(s);
-            checkAttValue(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkETag(ref string s, out string name) @safe pure // rule 42
-    {
-        mixin Check!("ETag");
-
-        try
-        {
-            checkLiteral("</",s);
-            checkName(s,name);
-            opt!(checkSpace)(s);
-            checkLiteral(">",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkContent(ref string s) @safe pure // rule 43
-    {
-        import std.algorithm.searching : startsWith;
-
-        mixin Check!("Content");
-
-        try
-        {
-            while (s.length != 0)
-            {
-                old = s;
-                     if (s.startsWith("&"))        { checkReference(s); }
-                else if (s.startsWith("<!--"))     { checkComment(s); }
-                else if (s.startsWith("<?"))       { checkPI(s); }
-                else if (s.startsWith(cdata)) { checkCDSect(s); }
-                else if (s.startsWith("</"))       { break; }
-                else if (s.startsWith("<"))        { checkElement(s); }
-                else                               { checkCharData(s); }
-            }
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkCharRef(ref string s, out dchar c) @safe pure // rule 66
-    {
-        import std.format : format;
-
-        mixin Check!("CharRef");
-
-        c = 0;
-        try { checkLiteral("&#",s); } catch (Err e) { fail(e); }
-        int radix = 10;
-        if (s.length != 0 && s[0] == 'x')
-        {
-            s = s[1..$];
-            radix = 16;
-        }
-        if (s.length == 0) fail("unterminated character reference");
-        if (s[0] == ';')
-            fail("character reference must have at least one digit");
-        while (s.length != 0)
-        {
-            immutable char d = s[0];
-            int n = 0;
-            switch (d)
-            {
-                case 'F','f': ++n;      goto case;
-                case 'E','e': ++n;      goto case;
-                case 'D','d': ++n;      goto case;
-                case 'C','c': ++n;      goto case;
-                case 'B','b': ++n;      goto case;
-                case 'A','a': ++n;      goto case;
-                case '9':     ++n;      goto case;
-                case '8':     ++n;      goto case;
-                case '7':     ++n;      goto case;
-                case '6':     ++n;      goto case;
-                case '5':     ++n;      goto case;
-                case '4':     ++n;      goto case;
-                case '3':     ++n;      goto case;
-                case '2':     ++n;      goto case;
-                case '1':     ++n;      goto case;
-                case '0':     break;
-                default: n = 100; break;
-            }
-            if (n >= radix) break;
-            c *= radix;
-            c += n;
-            s = s[1..$];
-        }
-        if (!isChar(c)) fail(format("U+%04X is not a legal character",c));
-        if (s.length == 0 || s[0] != ';') fail("expected ;");
-        else s = s[1..$];
-    }
-
-    void checkReference(ref string s) @safe pure // rule 67
-    {
-        import std.algorithm.searching : startsWith;
-
-        mixin Check!("Reference");
-
-        try
-        {
-            dchar c;
-            if (s.startsWith("&#")) checkCharRef(s,c);
-            else checkEntityRef(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkEntityRef(ref string s) @safe pure // rule 68
-    {
-        mixin Check!("EntityRef");
-
-        try
-        {
-            string name;
-            checkLiteral("&",s);
-            checkName(s,name);
-            checkLiteral(";",s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    void checkEncName(ref string s) @safe pure // rule 81
-    {
-        import std.algorithm.searching : countUntil;
-        import std.ascii : isAlpha;
-        import std.utf : byCodeUnit;
-
-        mixin Check!("EncName");
-
-        s = s[s.byCodeUnit.countUntil!(a => !isAlpha(a)) .. $];
-        if (s is old) fail();
-        s = s[s.byCodeUnit.countUntil('\"', '\'') .. $];
-    }
-
-    void checkEncodingDecl(ref string s) @safe pure // rule 80
-    {
-        mixin Check!("EncodingDecl");
-
-        try
-        {
-            checkSpace(s);
-            checkLiteral("encoding",s);
-            checkEq(s);
-            quoted!(checkEncName)(s);
-        }
-        catch (Err e) { fail(e); }
-    }
-
-    // Helper functions
-
-    void checkLiteral(string literal,ref string s) @safe pure
-    {
-        import std.string : startsWith;
-
-        mixin Check!("Literal");
-
-        if (!s.startsWith(literal)) fail("Expected literal \""~literal~"\"");
-        s = s[literal.length..$];
-    }
-
-    void checkEnd(string end,ref string s) @safe pure
-    {
-        import std.string : indexOf;
-        // Deliberately no mixin Check here.
-
-        auto n = s.indexOf(end);
-        if (n == -1) throw new Err(s,"Unable to find terminating \""~end~"\"");
-        s = s[n..$];
-        checkLiteral(end,s);
-    }
-
-    // Metafunctions -- none of these use mixin Check
-
-    void opt(alias f)(ref string s)
-    {
-        try { f(s); } catch (Err e) {}
-    }
-
-    void plus(alias f)(ref string s)
-    {
-        f(s);
-        star!(f)(s);
-    }
-
-    void star(alias f)(ref string s)
-    {
-        while (s.length != 0)
-        {
-            try { f(s); }
-            catch (Err e) { return; }
-        }
-    }
-
-    void quoted(alias f)(ref string s)
-    {
-        import std.string : startsWith;
-
-        if (s.startsWith("'"))
-        {
-            checkLiteral("'",s);
-            f(s);
-            checkLiteral("'",s);
-        }
-        else
-        {
-            checkLiteral("\"",s);
-            f(s);
-            checkLiteral("\"",s);
-        }
-    }
-
-    void seq(alias f,alias g)(ref string s)
-    {
-        f(s);
-        g(s);
-    }
-}
-
-/*
- * Check an entire XML document for well-formedness
- *
- * Params:
- *      s = the document to be checked, passed as a string
- *
- * Throws: CheckException if the document is not well formed
- *
- * CheckException's toString() method will yield the complete hierarchy of
- * parse failure (the XML equivalent of a stack trace), giving the line and
- * column number of every failure at every level.
- */
-void check(string s) @safe pure
-{
-    try
-    {
-        checkChars(s);
-        checkDocument(s);
-        if (s.length != 0) throw new Err(s,"Junk found after document");
-    }
-    catch (Err e)
-    {
-        e.complete(s);
-        throw e;
-    }
-}
-
-@system pure unittest
-{
-    import std.string : indexOf;
-
-    try
-    {
-        check(q"[<?xml version="1.0"?>
-        <catalog>
-           <book id="bk101">
-              <author>Gambardella, Matthew</author>
-              <title>XML Developer's Guide</title>
-              <genre>Computer</genre>
-              <price>44.95</price>
-              <publish_date>2000-10-01</publish_date>
-              <description>An in-depth look at creating applications
-              with XML.</description>
-           </book>
-           <book id="bk102">
-              <author>Ralls, Kim</author>
-              <title>Midnight Rain</title>
-              <genre>Fantasy</genres>
-              <price>5.95</price>
-              <publish_date>2000-12-16</publish_date>
-              <description>A former architect battles corporate zombies,
-              an evil sorceress, and her own childhood to become queen
-              of the world.</description>
-           </book>
-           <book id="bk103">
-              <author>Corets, Eva</author>
-              <title>Maeve Ascendant</title>
-              <genre>Fantasy</genre>
-              <price>5.95</price>
-              <publish_date>2000-11-17</publish_date>
-              <description>After the collapse of a nanotechnology
-              society in England, the young survivors lay the
-              foundation for a new society.</description>
-           </book>
-        </catalog>
-        ]");
-        assert(false);
-    }
-    catch (CheckException e)
-    {
-        auto n = e.toString().indexOf("end tag name \"genres\" differs"~
-                                      " from start tag name \"genre\"");
-        assert(n != -1);
-    }
-}
-
-@system unittest
-{
-    string s = q"EOS
-<?xml version="1.0"?>
-<set>
-    <one>A</one>
-    <!-- comment -->
-    <two>B</two>
-</set>
-EOS";
-    try
-    {
-        check(s);
-    }
-    catch (CheckException e)
-    {
-        assert(0, e.toString());
-    }
-}
-
-@system unittest
-{
-    string test_xml = `<?xml version="1.0" encoding='UTF-8'?><r><stream:stream
-                        xmlns:stream="http://etherx.'jabber'.org/streams"
-                        xmlns="jabber:'client'" from='jid.pl' id="587a5767"
-                        xml:lang="en" version="1.0" attr='a"b"c'>
-                        </stream:stream></r>`;
-
-    DocumentParser parser = new DocumentParser(test_xml);
-    bool tested = false;
-    parser.onStartTag["stream:stream"] = (ElementParser p) {
-        assert(p.tag.attr["xmlns"] == "jabber:'client'");
-        assert(p.tag.attr["from"] == "jid.pl");
-        assert(p.tag.attr["attr"] == "a\"b\"c");
-        tested = true;
-    };
-    parser.parse();
-    assert(tested);
-}
-
-@system unittest
-{
-    string s = q"EOS
-<?xml version="1.0" encoding="utf-8"?> <Tests>
-    <Test thing="What &amp; Up">What &amp; Up Second</Test>
-</Tests>
-EOS";
-    auto xml = new DocumentParser(s);
-
-    xml.onStartTag["Test"] = (ElementParser xml) {
-        assert(xml.tag.attr["thing"] == "What & Up");
-    };
-
-    xml.onEndTag["Test"] = (in Element e) {
-        assert(e.text() == "What & Up Second");
-    };
-    xml.parse();
-}
-
-@system unittest
-{
-    string s = `<tag attr="&quot;value&gt;" />`;
-    auto doc = new Document(s);
-    assert(doc.toString() == s);
-}
-
-/* The base class for exceptions thrown by this module */
-class XMLException : Exception { this(string msg) @safe pure { super(msg); } }
-
-// Other exceptions
-
-// Thrown during Comment constructor
-class CommentException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during CData constructor
-class CDataException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during XMLInstruction constructor
-class XIException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during ProcessingInstruction constructor
-class PIException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during Text constructor
-class TextException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during decode()
-class DecodeException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown if comparing with wrong type
-class InvalidTypeException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown when parsing for Tags
-class TagException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-/*
- * Thrown during check()
- */
-class CheckException : XMLException
-{
-    CheckException err; // Parent in hierarchy
-    private string tail;
-    /*
-     * Name of production rule which failed to parse,
-     * or specific error message
-     */
-    string msg;
-    size_t line = 0; // Line number at which parse failure occurred
-    size_t column = 0; // Column number at which parse failure occurred
-
-    private this(string tail,string msg,Err err=null) @safe pure
-    {
-        super(null);
-        this.tail = tail;
-        this.msg = msg;
-        this.err = err;
-    }
-
-    private void complete(string entire) @safe pure
-    {
-        import std.string : count, lastIndexOf;
-        import std.utf : toUTF32;
-
-        string head = entire[0..$-tail.length];
-        ptrdiff_t n = head.lastIndexOf('\n') + 1;
-        line = head.count("\n") + 1;
-        dstring t = toUTF32(head[n..$]);
-        column = t.length + 1;
-        if (err !is null) err.complete(entire);
-    }
-
-    override string toString() const @safe pure
-    {
-        import std.format : format;
-
-        string s;
-        if (line != 0) s = format("Line %d, column %d: ",line,column);
-        s ~= msg;
-        s ~= '\n';
-        if (err !is null) s = err.toString() ~ s;
-        return s;
-    }
-}
-
-private alias Err = CheckException;
-
-// Private helper functions
-
-private
-{
-    inout(T) toType(T)(inout return scope Object o)
-    {
-        T t = cast(T)(o);
-        if (t is null)
-        {
-            throw new InvalidTypeException("Attempt to compare a "
-                ~ T.stringof ~ " with an instance of another type");
-        }
-        return t;
-    }
-
-    string chop(ref string s, size_t n) @safe pure nothrow
-    {
-        if (n == -1) n = s.length;
-        string t = s[0 .. n];
-        s = s[n..$];
-        return t;
-    }
-
-    bool optc(ref string s, char c) @safe pure nothrow
-    {
-        immutable bool b = s.length != 0 && s[0] == c;
-        if (b) s = s[1..$];
-        return b;
-    }
-
-    void reqc(ref string s, char c) @safe pure
-    {
-        if (s.length == 0 || s[0] != c) throw new TagException("");
-        s = s[1..$];
-    }
-
-    char requireOneOf(ref string s, string chars) @safe pure
-    {
-        import std.string : indexOf;
-
-        if (s.length == 0 || indexOf(chars,s[0]) == -1)
-            throw new TagException("");
-        immutable char ch = s[0];
-        s = s[1..$];
-        return ch;
-    }
-
-    alias hash = .hashOf;
-
-    // Definitions from the XML specification
-    immutable CharTable=[0x9,0x9,0xA,0xA,0xD,0xD,0x20,0xD7FF,0xE000,0xFFFD,
-        0x10000,0x10FFFF];
-    immutable BaseCharTable=[0x0041,0x005A,0x0061,0x007A,0x00C0,0x00D6,0x00D8,
-        0x00F6,0x00F8,0x00FF,0x0100,0x0131,0x0134,0x013E,0x0141,0x0148,0x014A,
-        0x017E,0x0180,0x01C3,0x01CD,0x01F0,0x01F4,0x01F5,0x01FA,0x0217,0x0250,
-        0x02A8,0x02BB,0x02C1,0x0386,0x0386,0x0388,0x038A,0x038C,0x038C,0x038E,
-        0x03A1,0x03A3,0x03CE,0x03D0,0x03D6,0x03DA,0x03DA,0x03DC,0x03DC,0x03DE,
-        0x03DE,0x03E0,0x03E0,0x03E2,0x03F3,0x0401,0x040C,0x040E,0x044F,0x0451,
-        0x045C,0x045E,0x0481,0x0490,0x04C4,0x04C7,0x04C8,0x04CB,0x04CC,0x04D0,
-        0x04EB,0x04EE,0x04F5,0x04F8,0x04F9,0x0531,0x0556,0x0559,0x0559,0x0561,
-        0x0586,0x05D0,0x05EA,0x05F0,0x05F2,0x0621,0x063A,0x0641,0x064A,0x0671,
-        0x06B7,0x06BA,0x06BE,0x06C0,0x06CE,0x06D0,0x06D3,0x06D5,0x06D5,0x06E5,
-        0x06E6,0x0905,0x0939,0x093D,0x093D,0x0958,0x0961,0x0985,0x098C,0x098F,
-        0x0990,0x0993,0x09A8,0x09AA,0x09B0,0x09B2,0x09B2,0x09B6,0x09B9,0x09DC,
-        0x09DD,0x09DF,0x09E1,0x09F0,0x09F1,0x0A05,0x0A0A,0x0A0F,0x0A10,0x0A13,
-        0x0A28,0x0A2A,0x0A30,0x0A32,0x0A33,0x0A35,0x0A36,0x0A38,0x0A39,0x0A59,
-        0x0A5C,0x0A5E,0x0A5E,0x0A72,0x0A74,0x0A85,0x0A8B,0x0A8D,0x0A8D,0x0A8F,
-        0x0A91,0x0A93,0x0AA8,0x0AAA,0x0AB0,0x0AB2,0x0AB3,0x0AB5,0x0AB9,0x0ABD,
-        0x0ABD,0x0AE0,0x0AE0,0x0B05,0x0B0C,0x0B0F,0x0B10,0x0B13,0x0B28,0x0B2A,
-        0x0B30,0x0B32,0x0B33,0x0B36,0x0B39,0x0B3D,0x0B3D,0x0B5C,0x0B5D,0x0B5F,
-        0x0B61,0x0B85,0x0B8A,0x0B8E,0x0B90,0x0B92,0x0B95,0x0B99,0x0B9A,0x0B9C,
-        0x0B9C,0x0B9E,0x0B9F,0x0BA3,0x0BA4,0x0BA8,0x0BAA,0x0BAE,0x0BB5,0x0BB7,
-        0x0BB9,0x0C05,0x0C0C,0x0C0E,0x0C10,0x0C12,0x0C28,0x0C2A,0x0C33,0x0C35,
-        0x0C39,0x0C60,0x0C61,0x0C85,0x0C8C,0x0C8E,0x0C90,0x0C92,0x0CA8,0x0CAA,
-        0x0CB3,0x0CB5,0x0CB9,0x0CDE,0x0CDE,0x0CE0,0x0CE1,0x0D05,0x0D0C,0x0D0E,
-        0x0D10,0x0D12,0x0D28,0x0D2A,0x0D39,0x0D60,0x0D61,0x0E01,0x0E2E,0x0E30,
-        0x0E30,0x0E32,0x0E33,0x0E40,0x0E45,0x0E81,0x0E82,0x0E84,0x0E84,0x0E87,
-        0x0E88,0x0E8A,0x0E8A,0x0E8D,0x0E8D,0x0E94,0x0E97,0x0E99,0x0E9F,0x0EA1,
-        0x0EA3,0x0EA5,0x0EA5,0x0EA7,0x0EA7,0x0EAA,0x0EAB,0x0EAD,0x0EAE,0x0EB0,
-        0x0EB0,0x0EB2,0x0EB3,0x0EBD,0x0EBD,0x0EC0,0x0EC4,0x0F40,0x0F47,0x0F49,
-        0x0F69,0x10A0,0x10C5,0x10D0,0x10F6,0x1100,0x1100,0x1102,0x1103,0x1105,
-        0x1107,0x1109,0x1109,0x110B,0x110C,0x110E,0x1112,0x113C,0x113C,0x113E,
-        0x113E,0x1140,0x1140,0x114C,0x114C,0x114E,0x114E,0x1150,0x1150,0x1154,
-        0x1155,0x1159,0x1159,0x115F,0x1161,0x1163,0x1163,0x1165,0x1165,0x1167,
-        0x1167,0x1169,0x1169,0x116D,0x116E,0x1172,0x1173,0x1175,0x1175,0x119E,
-        0x119E,0x11A8,0x11A8,0x11AB,0x11AB,0x11AE,0x11AF,0x11B7,0x11B8,0x11BA,
-        0x11BA,0x11BC,0x11C2,0x11EB,0x11EB,0x11F0,0x11F0,0x11F9,0x11F9,0x1E00,
-        0x1E9B,0x1EA0,0x1EF9,0x1F00,0x1F15,0x1F18,0x1F1D,0x1F20,0x1F45,0x1F48,
-        0x1F4D,0x1F50,0x1F57,0x1F59,0x1F59,0x1F5B,0x1F5B,0x1F5D,0x1F5D,0x1F5F,
-        0x1F7D,0x1F80,0x1FB4,0x1FB6,0x1FBC,0x1FBE,0x1FBE,0x1FC2,0x1FC4,0x1FC6,
-        0x1FCC,0x1FD0,0x1FD3,0x1FD6,0x1FDB,0x1FE0,0x1FEC,0x1FF2,0x1FF4,0x1FF6,
-        0x1FFC,0x2126,0x2126,0x212A,0x212B,0x212E,0x212E,0x2180,0x2182,0x3041,
-        0x3094,0x30A1,0x30FA,0x3105,0x312C,0xAC00,0xD7A3];
-    immutable IdeographicTable=[0x3007,0x3007,0x3021,0x3029,0x4E00,0x9FA5];
-    immutable CombiningCharTable=[0x0300,0x0345,0x0360,0x0361,0x0483,0x0486,
-        0x0591,0x05A1,0x05A3,0x05B9,0x05BB,0x05BD,0x05BF,0x05BF,0x05C1,0x05C2,
-        0x05C4,0x05C4,0x064B,0x0652,0x0670,0x0670,0x06D6,0x06DC,0x06DD,0x06DF,
-        0x06E0,0x06E4,0x06E7,0x06E8,0x06EA,0x06ED,0x0901,0x0903,0x093C,0x093C,
-        0x093E,0x094C,0x094D,0x094D,0x0951,0x0954,0x0962,0x0963,0x0981,0x0983,
-        0x09BC,0x09BC,0x09BE,0x09BE,0x09BF,0x09BF,0x09C0,0x09C4,0x09C7,0x09C8,
-        0x09CB,0x09CD,0x09D7,0x09D7,0x09E2,0x09E3,0x0A02,0x0A02,0x0A3C,0x0A3C,
-        0x0A3E,0x0A3E,0x0A3F,0x0A3F,0x0A40,0x0A42,0x0A47,0x0A48,0x0A4B,0x0A4D,
-        0x0A70,0x0A71,0x0A81,0x0A83,0x0ABC,0x0ABC,0x0ABE,0x0AC5,0x0AC7,0x0AC9,
-        0x0ACB,0x0ACD,0x0B01,0x0B03,0x0B3C,0x0B3C,0x0B3E,0x0B43,0x0B47,0x0B48,
-        0x0B4B,0x0B4D,0x0B56,0x0B57,0x0B82,0x0B83,0x0BBE,0x0BC2,0x0BC6,0x0BC8,
-        0x0BCA,0x0BCD,0x0BD7,0x0BD7,0x0C01,0x0C03,0x0C3E,0x0C44,0x0C46,0x0C48,
-        0x0C4A,0x0C4D,0x0C55,0x0C56,0x0C82,0x0C83,0x0CBE,0x0CC4,0x0CC6,0x0CC8,
-        0x0CCA,0x0CCD,0x0CD5,0x0CD6,0x0D02,0x0D03,0x0D3E,0x0D43,0x0D46,0x0D48,
-        0x0D4A,0x0D4D,0x0D57,0x0D57,0x0E31,0x0E31,0x0E34,0x0E3A,0x0E47,0x0E4E,
-        0x0EB1,0x0EB1,0x0EB4,0x0EB9,0x0EBB,0x0EBC,0x0EC8,0x0ECD,0x0F18,0x0F19,
-        0x0F35,0x0F35,0x0F37,0x0F37,0x0F39,0x0F39,0x0F3E,0x0F3E,0x0F3F,0x0F3F,
-        0x0F71,0x0F84,0x0F86,0x0F8B,0x0F90,0x0F95,0x0F97,0x0F97,0x0F99,0x0FAD,
-        0x0FB1,0x0FB7,0x0FB9,0x0FB9,0x20D0,0x20DC,0x20E1,0x20E1,0x302A,0x302F,
-        0x3099,0x3099,0x309A,0x309A];
-    immutable DigitTable=[0x0030,0x0039,0x0660,0x0669,0x06F0,0x06F9,0x0966,
-        0x096F,0x09E6,0x09EF,0x0A66,0x0A6F,0x0AE6,0x0AEF,0x0B66,0x0B6F,0x0BE7,
-        0x0BEF,0x0C66,0x0C6F,0x0CE6,0x0CEF,0x0D66,0x0D6F,0x0E50,0x0E59,0x0ED0,
-        0x0ED9,0x0F20,0x0F29];
-    immutable ExtenderTable=[0x00B7,0x00B7,0x02D0,0x02D0,0x02D1,0x02D1,0x0387,
-        0x0387,0x0640,0x0640,0x0E46,0x0E46,0x0EC6,0x0EC6,0x3005,0x3005,0x3031,
-        0x3035,0x309D,0x309E,0x30FC,0x30FE];
-
-    bool lookup(const(int)[] table, int c) @safe @nogc nothrow pure
-    {
-        while (table.length != 0)
-        {
-            auto m = (table.length >> 1) & ~1;
-            if (c < table[m])
-            {
-                table = table[0 .. m];
-            }
-            else if (c > table[m+1])
-            {
-                table = table[m+2..$];
-            }
-            else return true;
-        }
-        return false;
-    }
-
-    string startOf(string s) @safe nothrow pure
-    {
-        string r;
-        foreach (char c;s)
-        {
-            r ~= (c < 0x20 || c > 0x7F) ? '.' : c;
-            if (r.length >= 40) { r ~= "___"; break; }
-        }
-        return r;
-    }
-
-    void exit(string s=null)
-    {
-        throw new XMLException(s);
-    }
-}
This page took 0.435608 seconds and 5 git commands to generate.