diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 467e886530417307a23b93d9f62a8c6ad3e39480..3e9067ea9c2b3fedc4d906721cde83125ae9d320 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -563,6 +563,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, global.params.previewIn = value; global.params.fix16997 = value; global.params.noSharedAccess = FeatureState::enabled; + global.params.safer = FeatureState::enabled; global.params.rvalueRefParam = FeatureState::enabled; global.params.inclusiveInContracts = value; global.params.systemVariables = FeatureState::enabled; @@ -613,6 +614,10 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, global.params.noSharedAccess = FeatureState::enabled; break; + case OPT_fpreview_safer: + global.params.safer = FeatureState::enabled; + break; + case OPT_fpreview_rvaluerefparam: global.params.rvalueRefParam = FeatureState::enabled; break; diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index f660884c3b9590a9f318b1d3025a5c9b36456315..bfdc9ea21e1e45e373e84f60baddf81994375890 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -2b89c2909de239bd603d6f36379658fe902667db +82a5d2a7c4dd3d270537bcede2981e047bfd0e6a The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index 1e96152d04a7ab9aa84a3de2103e9cfe40ccacc3..2e93d26fe853b56658023178aead5f5a4baae64b 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -254,7 +254,7 @@ Note that these groups have no strict meaning, the category assignments are a bi | [hdrgen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages | | [json.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag | | [dtoh.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d) | C++ header generation from D source files | -| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/disasm86.d) | x86-64 dissassembly generation +| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/x86/disasm86.d) | x86-64 disassembly generation | [disasmarm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/arm/disasmarm.d) | AArch64 disassembly generation ### Utility diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index caf58a20634cc1853707bf937f9a7d67a8bb5475..cb3afc6e62ea007530b05a22ca4a05efde1c05b8 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -80,7 +80,7 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) { eSink.error(e.loc, "%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); + errorSupplementalInferredAttr(f, 10, false, STC.nothrow_, eSink); import dmd.expressionsem : checkOverriddenDtor; f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isNothrow, "not nothrow"); diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 34b120b3412566845bbe91dc98524d764474b930..c15322f6b13f622354300b55e7ba353152bc9f85 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -2335,7 +2335,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); - if (e.hexString && !e.committed) + if (e.hexString && !e.committed && tb.nextOf().isIntegral) { const szx = cast(ubyte) tb.nextOf().size(); if (szx != se.sz && (e.len % szx) == 0) diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 4e04a272141ae64966db80b032c7d0c1f46ae497..f219e3f20279d7d97dd892d0ad56223a24f0d5d6 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -4855,33 +4855,6 @@ public: return; } - else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace) - { - // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`. - // The following code will rewrite it back to `ea ~= eb` and then interpret that expression. - Expression lhs, rhs; - - if (fd.ident == Id._d_arrayappendT) - { - assert(e.arguments.length == 2); - lhs = (*e.arguments)[0]; - rhs = (*e.arguments)[1]; - } - else - { - assert(e.arguments.length == 5); - lhs = (*e.arguments)[3]; - rhs = (*e.arguments)[4]; - } - - auto cae = new CatAssignExp(e.loc, lhs, rhs); - cae.type = e.type; - - result = interpretRegion(cae, istate, CTFEGoal.LValue); - return; - } - else if (fd.ident == Id._d_arrayappendcTX) - assert(0, "CTFE cannot interpret _d_arrayappendcTX!"); } else if (auto soe = ecall.isSymOffExp()) { @@ -6132,7 +6105,7 @@ public: { auto se = e1.isStringExp(); // Allow casting a hex string literal to short[], int[] or long[] - if (se && se.hexString && se.postfix == StringExp.NoPostfix) + if (se && se.hexString && se.postfix == StringExp.NoPostfix && e.to.nextOf().isIntegral) { const sz = cast(size_t) e.to.nextOf().size; if ((se.len % sz) != 0) @@ -6151,8 +6124,7 @@ public: } error(e.loc, "array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars()); if (se && se.hexString && se.postfix != StringExp.NoPostfix) - errorSupplemental(e.loc, "perhaps remove postfix `%s` from hex string", - (cast(char) se.postfix ~ "\0").ptr); + errorSupplemental(e.loc, "perhaps remove postfix `%.*s` from hex string", 1, &se.postfix); result = CTFEExp.cantexp; return; diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index 05cc8f156e11869336a0413da66b19ccc4aaf40d..aa48d57ba999e810c2d47c54c0012dd5cbd0fac8 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -64,21 +64,11 @@ private extern (D) struct BitFields Contract contract; bool ctfe; /// inside a ctfe-only expression bool traitsCompiles; /// inside __traits(compile) - /// ignore symbol visibility /// https://issues.dlang.org/show_bug.cgi?id=15907 bool ignoresymbolvisibility; - - bool _padding0; // To keep the layout the same as when the old `SCOPE` enum bitflags were used - bool inCfile; /// C semantics apply - - bool _padding1; - bool _padding2; - bool _padding3; - bool canFree; /// is on free list - bool fullinst; /// fully instantiate templates bool ctfeBlock; /// inside a `if (__ctfe)` block bool dip1000; /// dip1000 errors enabled for this scope diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d index 99211e4985484381d9eda6360783726da27a24cc..e22bdba34963d3d87b53a11d381ab0e5c870216d 100644 --- a/gcc/d/dmd/enumsem.d +++ b/gcc/d/dmd/enumsem.d @@ -185,7 +185,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) if (ed.members.length == 0) { - .error(ed.loc, "%s `%s enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars()); + .error(ed.loc, "%s `%s` enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars()); ed.errors = true; ed.semanticRun = PASS.semanticdone; return; diff --git a/gcc/d/dmd/errors.d b/gcc/d/dmd/errors.d index 79efc6e51aa45b77bfc84ea84f366aa0536095a0..1babb18bcc6b8477409b376decb0ae3c2cac4de4 100644 --- a/gcc/d/dmd/errors.d +++ b/gcc/d/dmd/errors.d @@ -37,60 +37,39 @@ class ErrorSinkCompiler : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) + void verror(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); - va_end(ap); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) + void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.error); - va_end(ap); } - void warning(const ref Loc loc, const(char)* format, ...) + void vwarning(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.warning); - va_end(ap); } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) + void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.warning); - va_end(ap); } - void deprecation(const ref Loc loc, const(char)* format, ...) + void vdeprecation(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.deprecation); - va_end(ap); } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation); - va_end(ap); } - void message(const ref Loc loc, const(char)* format, ...) + void vmessage(const ref Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.message); - va_end(ap); } } diff --git a/gcc/d/dmd/errorsink.d b/gcc/d/dmd/errorsink.d index afea689546b110b801fb3137430f3e019485f2c7..98841123ef1312636c5891831da7937a99c90b42 100644 --- a/gcc/d/dmd/errorsink.d +++ b/gcc/d/dmd/errorsink.d @@ -11,6 +11,8 @@ module dmd.errorsink; +import core.stdc.stdarg; + import dmd.location; /*************************************** @@ -21,19 +23,78 @@ abstract class ErrorSink nothrow: extern (C++): - void error(const ref Loc loc, const(char)* format, ...); + void verror(const ref Loc loc, const(char)* format, va_list ap); + void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap); + void vwarning(const ref Loc loc, const(char)* format, va_list ap); + void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap); + void vmessage(const ref Loc loc, const(char)* format, va_list ap); + void vdeprecation(const ref Loc loc, const(char)* format, va_list ap); + void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap); - void errorSupplemental(const ref Loc loc, const(char)* format, ...); + void error(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); + } - void warning(const ref Loc loc, const(char)* format, ...); + void errorSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + verrorSupplemental(loc, format, ap); + va_end(ap); + } - void warningSupplemental(const ref Loc loc, const(char)* format, ...); + void warning(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vwarning(loc, format, ap); + va_end(ap); + } + + void warningSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vwarningSupplemental(loc, format, ap); + va_end(ap); + } + + void message(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vmessage(loc, format, ap); + va_end(ap); + } - void message(const ref Loc loc, const(char)* format, ...); + void deprecation(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vdeprecation(loc, format, ap); + va_end(ap); + } - void deprecation(const ref Loc loc, const(char)* format, ...); + void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vdeprecationSupplemental(loc, format, ap); + va_end(ap); + } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...); + /** + * This will be called to indicate compilation has either + * finished or terminated, no more errors are possible - it's + * now the time to print any stored errors. + * + * The default implementation does nothing since most error sinks have no state + */ + void plugSink() {} } /***************************************** @@ -45,19 +106,19 @@ class ErrorSinkNull : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) { } + void verror(const ref Loc loc, const(char)* format, va_list ap) { } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) { } + void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap) { } - void warning(const ref Loc loc, const(char)* format, ...) { } + void vwarning(const ref Loc loc, const(char)* format, va_list ap) { } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap) { } - void message(const ref Loc loc, const(char)* format, ...) { } + void vmessage(const ref Loc loc, const(char)* format, va_list ap) { } - void deprecation(const ref Loc loc, const(char)* format, ...) { } + void vdeprecation(const ref Loc loc, const(char)* format, va_list ap) { } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap) { } } /***************************************** @@ -71,7 +132,7 @@ class ErrorSinkLatch : ErrorSinkNull bool sawErrors; - void error(const ref Loc loc, const(char)* format, ...) { sawErrors = true; } + void verror(const ref Loc loc, const(char)* format, va_list ap) { sawErrors = true; } } /***************************************** @@ -87,7 +148,7 @@ class ErrorSinkStderr : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) + void verror(const ref Loc loc, const(char)* format, va_list ap) { fputs("Error: ", stderr); const p = loc.toChars(); @@ -97,16 +158,13 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) { } + void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap) { } - void warning(const ref Loc loc, const(char)* format, ...) + void vwarning(const ref Loc loc, const(char)* format, va_list ap) { fputs("Warning: ", stderr); const p = loc.toChars(); @@ -116,16 +174,13 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap) { } - void deprecation(const ref Loc loc, const(char)* format, ...) + void vdeprecation(const ref Loc loc, const(char)* format, va_list ap) { fputs("Deprecation: ", stderr); const p = loc.toChars(); @@ -135,14 +190,11 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void message(const ref Loc loc, const(char)* format, ...) + void vmessage(const ref Loc loc, const(char)* format, va_list ap) { const p = loc.toChars(); if (*p) @@ -151,12 +203,9 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap) { } } diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index e1b2ef791a7f113ed6df88788e443387a6c8c504..3e18051afc12f0103919b0d309a4b84ab9b993c8 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -180,7 +180,7 @@ bool checkMutableArguments(ref Scope sc, FuncDeclaration fd, TypeFunction tf, if (!(eb.isMutable || eb2.isMutable)) return; - if (!tf.isLive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe())) + if (!tf.isLive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && setFunctionToUnsafe(sc.func))) return; if (!gag) diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 3c79b02caa026586eda134010582f14224aed1ba..dc72b3a8df1b7c0d3edd203f55f8ea8429ac068e 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -4143,10 +4143,9 @@ extern (C++) final class IndexExp : BinExp override bool isLvalue() { - if (e1.op == EXP.assocArrayLiteral) - return false; - if (e1.type.ty == Tsarray || - (e1.op == EXP.index && e1.type.ty != Tarray)) + auto t1b = e1.type.toBasetype(); + if (t1b.isTypeAArray() || t1b.isTypeSArray() || + (e1.isIndexExp() && t1b != t1b.isTypeDArray())) { return e1.isLvalue(); } diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index b26ce2380f9a2995bdeb92867dcdedc8ecefcf75..e1baa4887e7928792a1baac4ae1ff6050a0abc9e 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -1935,7 +1935,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) f.toPrettyChars()); if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_, global.errorSink); f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); return true; @@ -2018,15 +2018,18 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, } } -/// Print the reason why `fd` was inferred `@system` as a supplemental error -/// Params: -/// fd = function to check -/// maxDepth = up to how many functions deep to report errors -/// deprecation = print deprecations instead of errors -/// stc = storage class of attribute to check -public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) +/******************************************** + * Print the reason why `fd` was inferred `@system` as a supplemental error + * Params: + * fd = function to check + * maxDepth = up to how many functions deep to report errors + * deprecation = print deprecations instead of errors + * stc = storage class of attribute to check + * eSink = where the error messages go + */ +public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc, ErrorSink eSink) { - auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; + auto errorFunc = deprecation ? &eSink.deprecationSupplemental : &eSink.errorSupplemental; AttributeViolation* s; const(char)* attr; @@ -2054,7 +2057,7 @@ public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool if (!s) return; - if (s.fmtStr) + if (s.format) { errorFunc(s.loc, deprecation ? "which wouldn't be `%s` because of:" : @@ -2062,25 +2065,24 @@ public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool if (stc == STC.nogc || stc == STC.pure_) { auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); - errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); + errorFunc(s.loc, s.format, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); } else { - errorFunc(s.loc, s.fmtStr, + errorFunc(s.loc, s.format, s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); } } - else if (auto sa = s.arg0.isDsymbol()) + else if (s.fd) { - if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + if (maxDepth > 0) { - if (maxDepth > 0) - { - errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); - } + errorFunc(s.loc, "which calls `%s`", s.fd.toPrettyChars()); + errorSupplementalInferredAttr(s.fd, maxDepth - 1, deprecation, stc, eSink); } } + else + assert(0); } /******************************************* @@ -2262,7 +2264,7 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) sc.func.kind(), sc.func.toPrettyChars(), f.kind(), prettyChars); if (!f.isDtorDeclaration) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe, global.errorSink); .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); @@ -2276,12 +2278,12 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) if (sc.func.isSafeBypassingInference()) { .deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredAttr(f, 10, true, STC.safe); + errorSupplementalInferredAttr(f, 10, true, STC.safe, global.errorSink); } else if (!sc.func.safetyViolation) { import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null); + sc.func.safetyViolation = new AttributeViolation(loc, f); } } return false; @@ -2329,7 +2331,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); if (!f.isDtorDeclaration) - f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc, global.errorSink); } f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); @@ -2850,12 +2852,12 @@ private Expression rewriteOpAssign(BinExp exp) * Params: * sc = scope * argumentList = arguments to function - * reportErrors = whether or not to report errors here. Some callers are not + * eSink = if not null, used to report errors. Some callers are not * checking actual function params, so they'll do their own error reporting * Returns: * `true` when a semantic error occurred */ -private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const bool reportErrors = true) +private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, ErrorSink eSink) { Expressions* exps = argumentList.arguments; if (!exps) @@ -2876,9 +2878,9 @@ private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const b if (arg.op == EXP.type) { - if (reportErrors) + if (eSink) { - error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars()); + eSink.error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars()); arg = ErrorExp.get(); } err = true; @@ -2886,9 +2888,9 @@ private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const b } else if (arg.type.toBasetype().ty == Tfunction) { - if (reportErrors) + if (eSink) { - error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars()); + eSink.error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars()); arg = ErrorExp.get(); } err = true; @@ -2961,14 +2963,14 @@ private bool functionParameters(const ref Loc loc, Scope* sc, if (argumentList.names) { - const(char)* msg = null; - auto resolvedArgs = tf.resolveNamedArgs(argumentList, &msg); + OutBuffer buf; + auto resolvedArgs = tf.resolveNamedArgs(argumentList, &buf); if (!resolvedArgs) { // while errors are usually already caught by `tf.callMatch`, // this can happen when calling `typeof(freefunc)` - if (msg) - error(loc, "%s", msg); + if (buf.length) + error(loc, "%s", buf.peekChars()); return true; } // note: the argument list should be mutated with named arguments / default arguments, @@ -4974,7 +4976,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { return setError(); } - if (preFunctionParameters(sc, exp.argumentList)) + if (preFunctionParameters(sc, exp.argumentList, global.errorSink)) { return setError(); } @@ -5194,21 +5196,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto tiargs = new Objects(); tiargs.push(exp.newtype); - id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs); - id = new CallExp(exp.loc, id).expressionSemantic(sc); - Expression idVal; - Expression tmp = extractSideEffect(sc, "__tmpThrowable", idVal, id, true); - // auto castTmp = new CastExp(exp.loc, tmp, exp.type); + id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs); - auto ctor = new DotIdExp(exp.loc, tmp, Id.ctor).expressionSemantic(sc); - auto ctorCall = new CallExp(exp.loc, ctor, exp.arguments); + id = new CallExp(exp.loc, id).expressionSemantic(sc); - id = Expression.combine(idVal, exp.argprefix).expressionSemantic(sc); - id = Expression.combine(id, ctorCall).expressionSemantic(sc); - // id = Expression.combine(id, castTmp).expressionSemantic(sc); + exp.lowering = id.expressionSemantic(sc); - result = id.expressionSemantic(sc); + result = exp; return; } else if (sc.needsCodegen() && // interpreter doesn't need this lowered @@ -5648,7 +5643,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.ident != Id.empty) return; - const(char)[] s; + string s; if (exp.fd.fes) s = "__foreachbody"; else if (exp.fd.tok == TOK.reserved) @@ -5682,7 +5677,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor symtab = sds.symtab; } assert(symtab); - Identifier id = Identifier.generateId(s, symtab.length() + 1); + Identifier id = Identifier.generateIdWithLoc(s, exp.loc); exp.fd.ident = id; if (exp.td) exp.td.ident = id; @@ -5925,7 +5920,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (FuncExp fe = exp.e1.isFuncExp()) { if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) || - preFunctionParameters(sc, exp.argumentList)) + preFunctionParameters(sc, exp.argumentList, global.errorSink)) return setError(); // Run e1 semantic even if arguments have any errors @@ -6165,7 +6160,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) || - preFunctionParameters(sc, exp.argumentList)) + preFunctionParameters(sc, exp.argumentList, global.errorSink)) return setError(); // Check for call operator overload @@ -9334,7 +9329,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor "cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to)) { if (msg.length) - errorSupplemental(exp.loc, "%s", (msg ~ '\0').ptr); + errorSupplemental(exp.loc, "%.*s", msg.fTuple.expand); return setError(); } } @@ -9344,7 +9339,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor "cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to); // if message was printed if (sc.func && sc.func.isSafeBypassingInference() && !sc.isDeprecated()) - deprecationSupplemental(exp.loc, "%s", (msg ~ '\0').ptr); + deprecationSupplemental(exp.loc, "%.*s", msg.fTuple.expand); if (err) return setError(); } @@ -13083,9 +13078,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - override void visit(ShlExp exp) + private void visitShift(BinExp exp) { - //printf("ShlExp::semantic(), type = %p\n", type); if (exp.type) { result = exp; @@ -13112,87 +13106,32 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } + exp.e1 = integralPromotions(exp.e1, sc); if (exp.e2.type.toBasetype().ty != Tvector) - exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt); + { + Type tb1 = exp.e1.type.toBasetype(); + exp.e2 = exp.e2.castTo(sc, tb1.ty == Tvector ? tb1 : Type.tshiftcnt); + } exp.type = exp.e1.type; result = exp; } + override void visit(ShlExp exp) + { + visitShift(exp); + } override void visit(ShrExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - exp.e1 = integralPromotions(exp.e1, sc); - if (exp.e2.type.toBasetype().ty != Tvector) - exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt); - - exp.type = exp.e1.type; - result = exp; + visitShift(exp); } - override void visit(UshrExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - exp.e1 = integralPromotions(exp.e1, sc); - if (exp.e2.type.toBasetype().ty != Tvector) - exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt); - - exp.type = exp.e1.type; - result = exp; + visitShift(exp); } - override void visit(AndExp exp) + private void visitBinaryBitOp(BinExp exp) { if (exp.type) { @@ -13247,114 +13186,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp; } + override void visit(AndExp exp) + { + visitBinaryBitOp(exp); + } override void visit(OrExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool) - { - exp.type = exp.e1.type; - result = exp; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - result = exp; + visitBinaryBitOp(exp); } - override void visit(XorExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool) - { - exp.type = exp.e1.type; - result = exp; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - result = exp; + visitBinaryBitOp(exp); } override void visit(LogicalExp exp) @@ -15217,6 +15059,27 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) { return false; } + else if (sc._module.ident == Id.atomic && sc._module.parent !is null) + { + // Allow core.internal.atomic, it is an compiler implementation for a given platform module. + // It is then exposed by other modules such as core.atomic and core.stdc.atomic. + // This is available as long as druntime is on the import path and the platform supports that operation. + + // https://issues.dlang.org/show_bug.cgi?id=24846 + + Package parent = sc._module.parent.isPackage(); + if (parent !is null) + { + // This can be easily converted over to apply to core.atomic and core.internal.atomic + if (parent.ident == Id.internal) + { + parent = parent.parent.isPackage(); + + if (parent !is null && parent.ident == Id.core && parent.parent is null) + return false; + } + } + } //printf("checkSharedAccess() `%s` returnRef: %d\n", e.toChars(), returnRef); @@ -17184,8 +17047,11 @@ void semanticTypeInfo(Scope* sc, Type t) Scope scx; scx.eSink = global.errorSink; scx._module = sd.getModule(); - getTypeInfoType(sd.loc, t, &scx); - sd.requestTypeInfo = true; + if (global.params.useTypeInfo) + { + getTypeInfoType(sd.loc, t, &scx); + sd.requestTypeInfo = true; + } } else if (!sc.minst) { diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index e9b9367fd32ef0971cdc4acf20bed75799167b78..153befdb9a98b4cdf281e3ae0adcd74af7e0bdae 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -685,14 +685,15 @@ extern (C++) class FuncDeclaration : Declaration * * Params: * loc = location of action - * fmt = format string for error message + * format = format string for error message * arg0 = (optional) argument to format string */ - extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null) + extern (D) final void setThrow(Loc loc, const(char)* format, RootObject arg0 = null) { if (nothrowInprocess && !nothrowViolation) { - nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC + assert(format); + nothrowViolation = new AttributeViolation(loc, format, arg0); // action that requires GC } } @@ -700,11 +701,14 @@ extern (C++) class FuncDeclaration : Declaration * The function calls non-`nothrow` function f, register that in case nothrow is being inferred * Params: * loc = location of call - * f = function being called + * fd = function being called */ - extern (D) final void setThrowCall(Loc loc, FuncDeclaration f) + extern (D) final void setThrowCall(Loc loc, FuncDeclaration fd) { - return setThrow(loc, null, f); + if (nothrowInprocess && !nothrowViolation) + { + nothrowViolation = new AttributeViolation(loc, fd); // action that requires GC + } } /**************************************** @@ -1871,14 +1875,26 @@ extern (C++) final class NewDeclaration : FuncDeclaration /// The `FunctionDeclaration` is then stored in `arg0` and `fmtStr` must be `null`. struct AttributeViolation { - /// location of error - Loc loc = Loc.init; - /// printf-style format string - const(char)* fmtStr = null; - /// Arguments for up to two `%s` format specifiers in format string - RootObject arg0 = null; - /// ditto - RootObject arg1 = null; - /// ditto - RootObject arg2 = null; + Loc loc; /// location of error + + FuncDeclaration fd; /// function is the focus of the violation + + // -- OR -- + + const(char)* format; /// printf-style format string + RootObject arg0; /// Arguments for up to two `%s` format specifiers in format string + RootObject arg1; /// ditto + RootObject arg2; /// ditto + + this(ref Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; } + + this(ref Loc loc, const(char)* format, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) + { + assert(format); + this.loc = loc; + this.format = format; + this.arg0 = arg0; + this.arg1 = arg1; + this.arg2 = arg2; + } } diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index c142a16f065da237782a1b29e1a3f16cbf521e4b..bfa0faca60f315580f881c2d3fc8b8d1e70d032c 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -3001,7 +3001,15 @@ extern (D) bool setImpure(FuncDeclaration fd, Loc loc = Loc.init, const(char)* f if (fmt) fd.pureViolation = new AttributeViolation(loc, fmt, fd, arg0); // impure action else if (arg0) - fd.pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function + { + if (auto sa = arg0.isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + fd.pureViolation = new AttributeViolation(loc, fd2); // call to impure function + } + } + } if (fd.fes) fd.fes.func.setImpure(loc, fmt, arg0); diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index ccb63e330e9e5879b67bd65585e1c173461c9895..88b27d20bb54ef6c00e7229883a39f87a9d63d8b 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -188,6 +188,8 @@ extern (C++) struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md FeatureState noSharedAccess; // read/write access to shared memory objects bool previewIn; // `in` means `[ref] scope const`, accepts rvalues bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract @@ -467,6 +469,17 @@ extern (C++) struct Global return major * 1000 + minor; } + /** + * Indicate to stateful error sinks that no more errors can be produced. + * This is to support error sinks that collect information to produce a + * single (say) report. + */ + extern(C++) void plugErrorSinks() + { + global.errorSink.plugSink(); + global.errorSinkNull.plugSink(); + } + /** Returns: the version as the number that would be returned for __VERSION__ */ diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 5348d614ebb3a64429042b805fd668bdef9e6136..c5659ea10b6268f03cd62f2c65d161a813e5fe08 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -191,6 +191,9 @@ struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md + FeatureState noSharedAccess; // read/write access to shared memory objects d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract @@ -350,6 +353,13 @@ struct Global void _init(); + /** + * Indicate to stateful error sinks that no more errors can be produced. + * This is to support error sinks that collect information to produce a + * single (say) report. + */ + void plugErrorSinks(); + /** Returns: the version as the number that would be returned for __VERSION__ */ diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index f676361d958bba49dda83dfa6df59cefa7219a52..aae07bc15370371555ba0f888081a5ced1338f7d 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -389,6 +389,7 @@ immutable Msgtable[] msgtable = // Builtin functions { "std" }, { "core" }, + { "internal" }, { "config" }, { "c_complex_float" }, { "c_complex_double" }, @@ -481,9 +482,12 @@ immutable Msgtable[] msgtable = { "isRef" }, { "isOut" }, { "isLazy" }, + { "isCOMClass" }, { "hasMember" }, { "identifier" }, { "fullyQualifiedName" }, + { "getBitfieldOffset" }, + { "getBitfieldWidth" }, { "getProtection" }, { "getVisibility" }, { "parent" }, diff --git a/gcc/d/dmd/intrange.d b/gcc/d/dmd/intrange.d index 51f28013f22bd2c9a8b83b366cbaffd81e8dbc8e..d89fbb2ffc1121946ea7e787952ed34b8fd61fde 100644 --- a/gcc/d/dmd/intrange.d +++ b/gcc/d/dmd/intrange.d @@ -13,10 +13,9 @@ module dmd.intrange; import core.stdc.stdio; -import dmd.astenums; -import dmd.mtype; -import dmd.expression; -import dmd.globals; +import dmd.astenums : Tdchar; +import dmd.mtype : Type; +import dmd.globals : uinteger_t; private uinteger_t copySign(uinteger_t x, bool sign) @safe { @@ -322,7 +321,7 @@ struct IntRange static IntRange fromType(Type type, bool isUnsigned) { - if (!type.isIntegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return widest(); uinteger_t mask = type.sizemask(); @@ -444,7 +443,7 @@ struct IntRange IntRange _cast(Type type) { - if (!type.isIntegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return this; else if (!type.isUnsigned()) return castSigned(type.sizemask()); @@ -456,7 +455,7 @@ struct IntRange IntRange castUnsigned(Type type) { - if (!type.isIntegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return castUnsigned(ulong.max); else if (type.toBasetype().ty == Tdchar) return castDchar(); @@ -504,7 +503,7 @@ struct IntRange union_ = true; } - ref const(IntRange) dump(const(char)* funcName, Expression e) const return + ref const(IntRange) dump(Exp)(const(char)* funcName, Exp e) const return { printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n", imin.negative?'-':'+', cast(ulong)imin.value, @@ -668,7 +667,7 @@ struct IntRange return widest(); // Don't treat the whole range as divide by 0 if only one end of a range is 0. - // Issue 15289 + // https://issues.dlang.org/show_bug.cgi?id=15289 if (rhs.imax.value == 0) { rhs.imax.value--; @@ -682,6 +681,11 @@ struct IntRange { return IntRange(imin / rhs.imax, imax / rhs.imin); } + else if (rhs.imin.negative && !rhs.imax.negative) // divisor spans [-1, 0, 1] + { + SignExtendedNumber[4] bdy = [-imin, imin, -imax, imax]; + return IntRange.fromNumbers4(bdy.ptr); + } else { // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 476a700bdeb7da13b58663130c87aff85ce1c751..d9f4b1c678f655a040869ad0dc76549dca2f7474 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -1555,6 +1555,8 @@ class Lexer if (ndigits != 2 && !utf_isValidDchar(v)) { error(loc, "invalid UTF character \\U%08x", v); + if (v >= 0xD800 && v <= 0xDFFF) + errorSupplemental("The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?"); v = '?'; // recover with valid UTF character } } @@ -3168,6 +3170,11 @@ class Lexer eSink.error(loc, format, args); } + void errorSupplemental(T...)(const(char)* format, T args) + { + eSink.errorSupplemental(token.loc, format, args); + } + void deprecation(T...)(const ref Loc loc, const(char)* format, T args) { eSink.deprecation(loc, format, args); @@ -3434,13 +3441,14 @@ class Lexer if (*q != ct) break; } - /* Remove leading spaces until start of the comment + /* Remove leading line feed or space */ int linestart = 0; if (ct == '/') { - while (q < qend && (*q == ' ' || *q == '\t')) + if (q < qend && *q == ' ') { ++q; + } } else if (q < qend) { @@ -3671,25 +3679,35 @@ unittest import core.stdc.stdarg; string expected; + string expectedSupplemental; bool gotError; - void error(const ref Loc loc, const(char)* format, ...) + void verror(const ref Loc loc, const(char)* format, va_list ap) { gotError = true; char[100] buffer = void; + auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)]; + assert(expected == actual); + } + + void errorSupplemental(const ref Loc loc, const(char)* format, ...) + { + gotError = true; + char[128] buffer = void; va_list ap; va_start(ap, format); auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)]; va_end(ap); - assert(expected == actual); + assert(expectedSupplemental == actual); } } ErrorSinkTest errorSink = new ErrorSinkTest; - void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false) + void test2(string sequence, string[2] expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false) { - errorSink.expected = expectedError; + errorSink.expected = expectedError[0]; + errorSink.expectedSupplemental = expectedError[1]; errorSink.gotError = false; auto p = cast(const(char)*)sequence.ptr; Lexer lexer = new Lexer(errorSink); @@ -3702,6 +3720,11 @@ unittest assert(expectedScanLength == actualScanLength); } + void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false) + { + test2(sequence, [expectedError, null], expectedReturnValue, expectedScanLength, Ccompile); + } + test("c", `undefined escape sequence \c`, 'c', 1); test("!", `undefined escape sequence \!`, '!', 1); test(""", `undefined escape sequence \&`, '&', 1, true); @@ -3720,8 +3743,6 @@ unittest test("U0001f6" , `escape hex sequence has 6 hex digits instead of 8`, 0x0001f6, 7); test("U0001f60", `escape hex sequence has 7 hex digits instead of 8`, 0x0001f60, 8); - test("ud800" , `invalid UTF character \U0000d800`, '?', 5); - test("udfff" , `invalid UTF character \U0000dfff`, '?', 5); test("U00110000", `invalid UTF character \U00110000`, '?', 9); test("xg0" , `undefined escape hex sequence \xg`, 'g', 2); @@ -3733,6 +3754,9 @@ unittest test(""", `unterminated named entity "`, '?', 5); test("400", `escape octal sequence \400 is larger than \377`, 0x100, 3); + + test2("uD800", [`invalid UTF character \U0000d800`, `The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?`], '?', 5); + test2("uDFFF", [`invalid UTF character \U0000dfff`, `The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?`], '?', 5); } unittest diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index be70b02dec90c4489f711e704396671330c5ec04..6d57467314ce04606a76d1fc20f543b51ab9c8c1 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -2602,13 +2602,20 @@ extern (C++) final class TypeFunction : TypeNext return linkage == LINK.d && parameterList.varargs == VarArg.variadic; } - extern(D) static const(char)* getMatchError(A...)(const(char)* format, A args) + /********************************* + * Append error message to buf. + * Input: + * buf = message sink + * format = printf format + */ + extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...) { if (global.gag && !global.params.v.showGaggedErrors) - return null; - OutBuffer buf; - buf.printf(format, args); - return buf.extractChars(); + return; + va_list ap; + va_start(ap, format); + buf.vprintf(format, ap); + va_end(ap); } /******************************** @@ -2617,10 +2624,10 @@ extern (C++) final class TypeFunction : TypeNext * * Params: * argumentList = array of function arguments - * pMessage = address to store error message, or `null` + * buf = if not null, append error message to it * Returns: re-ordered argument list, or `null` on error */ - extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, const(char)** pMessage) + extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf) { Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null; Identifier[] names = argumentList.names ? (*argumentList.names)[] : null; @@ -2644,8 +2651,8 @@ extern (C++) final class TypeFunction : TypeNext const pi = findParameterIndex(name); if (pi == -1) { - if (pMessage) - *pMessage = getMatchError("no parameter named `%s`", name.toChars()); + if (buf) + getMatchError(*buf, "no parameter named `%s`", name.toChars()); return null; } ci = pi; @@ -2655,8 +2662,8 @@ extern (C++) final class TypeFunction : TypeNext if (!isVariadic) { // Without named args, let the caller diagnose argument overflow - if (hasNamedArgs && pMessage) - *pMessage = getMatchError("argument `%s` goes past end of parameter list", arg.toChars()); + if (hasNamedArgs && buf) + getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars()); return null; } while (ci >= newArgs.length) @@ -2665,8 +2672,8 @@ extern (C++) final class TypeFunction : TypeNext if ((*newArgs)[ci]) { - if (pMessage) - *pMessage = getMatchError("parameter `%s` assigned twice", parameterList[ci].toChars()); + if (buf) + getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars()); return null; } (*newArgs)[ci++] = arg; @@ -2684,8 +2691,8 @@ extern (C++) final class TypeFunction : TypeNext if (this.incomplete) continue; - if (pMessage) - *pMessage = getMatchError("missing argument for parameter #%d: `%s`", + if (buf) + getMatchError(*buf, "missing argument for parameter #%d: `%s`", i + 1, parameterToChars(parameterList[i], this, false)); return null; } diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d index cedc4a43c8f15867cb864979a02261c1849d62ff..4a060c9330656952d50c9e904673ce157cb0ecd9 100644 --- a/gcc/d/dmd/nogc.d +++ b/gcc/d/dmd/nogc.d @@ -302,7 +302,15 @@ extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject if (fmt) fd.nogcViolation = new AttributeViolation(loc, fmt, fd, arg0); // action that requires GC else if (arg0) - fd.nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function + { + if (auto sa = arg0.isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + fd.nogcViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function + } + } + } fd.type.toTypeFunction().isNogc = false; if (fd.fes) diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index e8324eb0710654db7f9984e2581f4094bf96fa39..3e145be5499ff8de3bbab926d485190cede09535 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -6812,7 +6812,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { case TOK.identifier: { - if (commaExpected) error("comma expected separating field initializers"); const t = peek(&token); @@ -6846,6 +6845,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: if (commaExpected) error("comma expected separating field initializers"); + const t = peek(&token); + if (t.value == TOK.colon) + { + error("incorrect syntax for associative array, expected `[]`, found `{}`"); + while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) + { + nextToken(); + } + if (token.value == TOK.rightCurly) + { + nextToken(); + } + break; + } auto value = parseInitializer(); _is.addInit(null, value); commaExpected = true; @@ -8647,7 +8660,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("expression expected, not `%s`", token.toChars()); Lerr: // Anything for e, as long as it's not NULL - e = new AST.IntegerExp(loc, 0, AST.Type.tint32); + e = AST.ErrorExp.get(); nextToken(); break; } diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d index 81355774de3d0c36a35f38d072cdee8a5d86abc9..6e7db065e7ec81566dc42b596eef045c9c389ec9 100644 --- a/gcc/d/dmd/root/array.d +++ b/gcc/d/dmd/root/array.d @@ -52,7 +52,7 @@ public: ~this() pure nothrow { debug (stomp) memset(data.ptr, 0xFF, data.length); - if (data.ptr != &smallarray[0]) + if (data.ptr && data.ptr != &smallarray[0]) mem.xfree(data.ptr); } ///returns elements comma separated in [] diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index 24d87458551655a2595f0b68f2a3117e49aa19c7..86dff7b5bcf7db13e6691b1366a0b620455885cc 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -21,12 +21,13 @@ import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dscope; +import dmd.dsymbol; import dmd.dsymbolsem : determineSize; import dmd.errors; import dmd.expression; import dmd.func; import dmd.funcsem : isRootTraitsCompilesScope; -import dmd.globals : FeatureState; +import dmd.globals : FeatureState, global; import dmd.id; import dmd.identifier; import dmd.location; @@ -316,10 +317,20 @@ bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag) return false; } +/************************************** + * Safer D adds safety checks to functions with the default + * trust setting. + */ +bool isSaferD(FuncDeclaration fd) +{ + return fd.type.toTypeFunction().trust == TRUST.default_ && + global.params.safer == FeatureState.enabled; +} + bool isSafe(FuncDeclaration fd) { if (fd.safetyInprocess) - fd.setUnsafe(); + setFunctionToUnsafe(fd); return fd.type.toTypeFunction().trust == TRUST.safe; } @@ -331,48 +342,73 @@ extern (D) bool isSafeBypassingInference(FuncDeclaration fd) bool isTrusted(FuncDeclaration fd) { if (fd.safetyInprocess) - fd.setUnsafe(); + setFunctionToUnsafe(fd); return fd.type.toTypeFunction().trust == TRUST.trusted; } -/************************************** - * The function is doing something unsafe, so mark it as unsafe. - * +/***************************************************** + * Report safety violation for function `fd`, or squirrel away + * error message in fd.safetyViolation if needed later. + * Call when `fd` was just inferred to be @system OR + * `fd` was @safe and an tried something unsafe. * Params: - * fd = func declaration to set unsafe - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string + * fd = function we're gonna rat on + * gag = suppress error message (used in escape.d) + * loc = location of error + * format = printf-style format string * arg0 = (optional) argument for first %s format specifier * arg1 = (optional) argument for second %s format specifier * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error */ -extern (D) bool setUnsafe( - FuncDeclaration fd, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc, + const(char)* format = null, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + if (fd.type.toTypeFunction().trust == TRUST.system) // function was just inferred to be @system + { + if (format) + fd.safetyViolation = new AttributeViolation(loc, format, arg0, arg1, arg2); + else if (arg0) + { + if (FuncDeclaration fd2 = (cast(Dsymbol) arg0).isFuncDeclaration()) + { + fd.safetyViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function + } + } + } + else if (fd.isSafe() || fd.isSaferD()) + { + if (!gag && format) + .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + } +} + + +/********************************************** + * Function is doing something unsafe. If inference + * is in process, commit the function to be @system. + * Params: + * fd = the naughty function + * Returns: + * true if this is a safe function and so an error OR is inferred to be @system, + * false otherwise. + */ +extern (D) bool setFunctionToUnsafe(FuncDeclaration fd) { if (fd.safetyInprocess) { fd.safetyInprocess = false; fd.type.toTypeFunction().trust = TRUST.system; - if (fmt || arg0) - fd.safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); if (fd.fes) - fd.fes.func.setUnsafe(); - } - else if (fd.isSafe()) - { - if (!gag && fmt) - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - + setFunctionToUnsafe(fd.fes.func); return true; } + else if (fd.isSafe() || fd.isSaferD()) + return true; return false; } + /************************************** * The function is calling `@system` function `f`, so mark it as unsafe. * @@ -383,7 +419,12 @@ extern (D) bool setUnsafe( */ extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f) { - return fd.setUnsafe(false, f.loc, null, f, null); + if (setFunctionToUnsafe(fd)) + { + reportSafeError(fd, false, f.loc, null, f, null); + return fd.isSafe(); + } + return false; } /************************************** @@ -394,14 +435,14 @@ extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f) * sc = scope that the unsafe statement / expression is in * gag = surpress error message (used in escape.d) * loc = location of error - * fmt = printf-style format string + * format = printf-style format string * arg0 = (optional) argument for first %s format specifier * arg1 = (optional) argument for second %s format specifier * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error + * Returns: whether there is a safe error */ bool setUnsafe(Scope* sc, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, + bool gag = false, Loc loc = Loc.init, const(char)* format = null, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) { if (sc.intypeof) @@ -416,7 +457,7 @@ bool setUnsafe(Scope* sc, { if (sc.varDecl.storage_class & STC.safe) { - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); return true; } else if (!(sc.varDecl.storage_class & STC.trusted)) @@ -435,13 +476,21 @@ bool setUnsafe(Scope* sc, { // Message wil be gagged, but still call error() to update global.errors and for // -verrors=spec - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); return true; } return false; } - return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); + if (setFunctionToUnsafe(sc.func)) + { + if (format || arg0) + { + reportSafeError(sc.func, gag, loc, format, arg0, arg1, arg2); + } + return sc.func.isSafe(); // it is only an error if in an @safe function + } + return false; } /*************************************** @@ -459,23 +508,24 @@ bool setUnsafe(Scope* sc, * fs = feature state from the preview flag * gag = surpress error message * loc = location of error - * msg = printf-style format string + * format = printf-style format string * arg0 = (optional) argument for first %s format specifier * arg1 = (optional) argument for second %s format specifier * arg2 = (optional) argument for third %s format specifier * Returns: whether an actual safe error (not deprecation) occured */ -bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, +bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* format, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) { - //printf("setUnsafePreview() fs:%d %s\n", fs, msg); + //printf("setUnsafePreview() fs:%d %s\n", fs, fmt); + assert(format); with (FeatureState) final switch (fs) { case disabled: return false; case enabled: - return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); + return sc.setUnsafe(gag, loc, format, arg0, arg1, arg2); case default_: if (!sc.func) @@ -484,13 +534,13 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char) { if (!gag && !sc.isDeprecated()) { - deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + deprecation(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); } } else if (!sc.func.safetyViolation) { import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); + sc.func.safetyViolation = new AttributeViolation(loc, format, arg0, arg1, arg2); } return false; } diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index c584b846489060fa8287f9dfa167acb47043b982..8138bd2eb952711efe458d98d6ae7b93f0547b6a 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -57,7 +57,7 @@ import dmd.opover; import dmd.parse; import dmd.common.outbuffer; import dmd.root.string; -import dmd.safe : isSafe, setUnsafe; +import dmd.safe : isSafe, isSaferD, setUnsafe; import dmd.semantic2; import dmd.sideeffect; import dmd.statement; @@ -779,14 +779,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors - /* Check for inference errors - */ + /* Check for inference errors and apply mutability checks inline */ if (!inferApplyArgTypes(fs, sc, sapply)) { - /** - Try and extract the parameter count of the opApply callback function, e.g.: - int opApply(int delegate(int, float)) => 2 args - */ bool foundMismatch = false; size_t foreachParamCount = 0; if (sapplyOld) @@ -806,6 +801,19 @@ Statement statementSemanticVisit(Statement s, Scope* sc) auto tf = fparam.type.nextOf().isTypeFunction(); foreachParamCount = tf.parameterList.length; foundMismatch = true; + + // Mutability check + if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst()) + { + // First error: The call site + error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object", + fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars()); + + // Second error: Suggest how to fix + errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + + return setError(); + } } } } @@ -824,6 +832,24 @@ Statement statementSemanticVisit(Statement s, Scope* sc) return setError(); } + // If inference succeeds, proceed with post-checks + if (sapply && sapply.isFuncDeclaration()) + { + FuncDeclaration fd = sapply.isFuncDeclaration(); + + if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst()) + { + // First error: The call site + error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object", + fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars()); + + // Second error: Suggest how to fix + errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + + return setError(); + } + } + Type tab = fs.aggr.type.toBasetype(); if (tab.ty == Ttuple) // don't generate new scope for tuple loops @@ -1961,7 +1987,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } ss.hasDefault = sc.sw.sdefault || - !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe); + !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe || sc.func.isSaferD); if (!ss.hasDefault) { if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile) diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d index bd3674a8cc8260b39c46ce0e7b54bc04ca3da070..0e9c4338a05203214b4b37beaa042e99ce7f8605 100644 --- a/gcc/d/dmd/templatesem.d +++ b/gcc/d/dmd/templatesem.d @@ -1252,7 +1252,13 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars()); RootObject oarg = farg; - if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) + + if (farg.isFuncExp()) + { + // When assigning an untyped (void) lambda `x => y` to a `(F)(ref F)` parameter, + // we don't want to deduce type void creating a void parameter + } + else if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) { /* Allow expressions that have CT-known boundaries and type [] to match with [dim] */ diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 7b32209fd2ef7fecdb69de99968823113dd64119..f7f4cd2bb5e235a9ac258bdbf579ed8a774cb611 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -701,6 +701,26 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return isDeclX(d => (d.storage_class & STC.lazy_) != 0); } + if (e.ident == Id.isCOMClass) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + auto s = getDsymbol(o); + AggregateDeclaration agg; + + if (!s || ((agg = s.isAggregateDeclaration()) is null)) + { + error(e.loc, "argument to `__traits(isCOMClass, %s)` is not a declaration", o.toChars()); + return ErrorExp.get(); + } + + if (ClassDeclaration cd = agg.isClassDeclaration()) + return cd.com ? True() : False(); + else + return False(); + } if (e.ident == Id.identifier) { // Get identifier for symbol as a string literal @@ -776,6 +796,43 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return se.expressionSemantic(sc); } + if (e.ident == Id.getBitfieldOffset || e.ident == Id.getBitfieldWidth) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + auto s = getDsymbolWithoutExpCtx(o); + if (!s) + { + error(e.loc, "bitfield symbol expected not `%s`", o.toChars()); + return ErrorExp.get(); + } + + auto vd = s.toAlias.isVarDeclaration(); + if (!vd || !(vd.storage_class & STC.field)) + { + error(e.loc, "bitfield symbol expected not %s `%s`", s.kind, s.toPrettyChars); + return ErrorExp.get(); + } + + uint fieldWidth; + uint bitOffset; + if (auto bf = vd.isBitFieldDeclaration()) + { + fieldWidth = bf.fieldWidth; + bitOffset = bf.bitOffset; + } + else // just a regular field + { + const sz = size(vd.type); + assert(sz < uint.max / 8); // overflow check + fieldWidth = cast(uint)sz * 8; + bitOffset = 0; + } + uint value = e.ident == Id.getBitfieldOffset ? bitOffset : fieldWidth; + return new IntegerExp(e.loc, value, Type.tuns32); + } if (e.ident == Id.getProtection || e.ident == Id.getVisibility) { if (dim != 1) diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index eebbf45573dc32c15ae3712d645569a0c5d6a4e0..aea969a2e08df4746c8d93b578111cdd695182c7 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -753,13 +753,14 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis } const(char)* failMessage; const(char)** pMessage = errorHelper ? &failMessage : null; - auto resolvedArgs = tf.resolveNamedArgs(argumentList, pMessage); + OutBuffer buf; + auto resolvedArgs = tf.resolveNamedArgs(argumentList, errorHelper ? &buf : null); Expression[] args; if (!resolvedArgs) { - if (failMessage) + if (buf.length) { - errorHelper(failMessage); + errorHelper(buf.peekChars()); return MATCH.nomatch; } @@ -820,6 +821,11 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis if (!arg) continue; // default argument m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage); + if (failMessage) + { + buf.reset(); + buf.writestring(failMessage); + } } else if (p.defaultArg) continue; @@ -846,15 +852,17 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis errorHelper(failMessage); return MATCH.nomatch; } - if (pMessage && u >= args.length) - *pMessage = tf.getMatchError("missing argument for parameter #%d: `%s`", - u + 1, parameterToChars(p, tf, false)); - // If an error happened previously, `pMessage` was already filled - else if (pMessage && !*pMessage) - *pMessage = tf.getParamError(args[u], p); - if (errorHelper) - errorHelper(*pMessage); + { + if (u >= args.length) + TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`", + u + 1, parameterToChars(p, tf, false)); + // If an error happened previously, `pMessage` was already filled + else if (buf.length == 0) + buf.writestring(tf.getParamError(args[u], p)); + + errorHelper(buf.peekChars()); + } return MATCH.nomatch; } if (m < match) @@ -864,7 +872,9 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis if (errorHelper && !parameterList.varargs && args.length > nparams) { // all parameters had a match, but there are surplus args - errorHelper(tf.getMatchError("expected %d argument(s), not %d", nparams, args.length)); + OutBuffer buf2; + TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length); + errorHelper(buf2.extractChars()); return MATCH.nomatch; } //printf("match = %d\n", match); @@ -1174,8 +1184,12 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, if (sz != trailingArgs.length) { if (pMessage) - *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu", + { + OutBuffer buf; + TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu", sz, trailingArgs.length); + *pMessage = buf.extractChars(); + } return MATCH.nomatch; } goto case Tarray; diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index ff09c0927158f8fc0675a10b5eaa34b2901d5fe5..304d6bd203c5898cb6ac9b6121d415be331796d8 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -2246,15 +2246,10 @@ public: new_call = build_nop (type, build_address (var)); setup_exp = modify_expr (var, aggregate_initializer_decl (cd)); } - else if (global.params.ehnogc && e->thrownew) - { - /* Allocating a `@nogc' Exception with `_d_newThrowable' has already - been handled by the front-end. */ - gcc_unreachable (); - } else { - /* Generate: _d_newclass() */ + /* Generate: _d_newclass() + or: _d_newThrowable() */ new_call = build_expr (e->lowering); } diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index b101a3a73c7f012a192249729f801a4363023da5..f769a71aee81deff9bebdfc03824adcca68d2131 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -400,6 +400,10 @@ fpreview=nosharedaccess D RejectNegative Disable access to shared memory objects. +fpreview=safer +D RejectNegative +Enable safety checks on all functions by default. + fpreview=rvaluerefparam D RejectNegative Enable rvalue arguments to ref parameters. diff --git a/gcc/testsuite/gdc.test/compilable/atomic_store_2_shared_classes.d b/gcc/testsuite/gdc.test/compilable/atomic_loadstore_shared_classes.d similarity index 60% rename from gcc/testsuite/gdc.test/compilable/atomic_store_2_shared_classes.d rename to gcc/testsuite/gdc.test/compilable/atomic_loadstore_shared_classes.d index 0d8cd7489376c9c5520758472d0c1dc997e8a790..f719aa829f0207e401e99596d61eacbefe6100c4 100644 --- a/gcc/testsuite/gdc.test/compilable/atomic_store_2_shared_classes.d +++ b/gcc/testsuite/gdc.test/compilable/atomic_loadstore_shared_classes.d @@ -5,9 +5,14 @@ class Foo { } +shared Foo toLoad; + void oops() { auto f0 = new shared Foo; auto f1 = new shared Foo; atomicStore(f0, f1); + + // https://issues.dlang.org/show_bug.cgi?id=24846 + shared(Foo) f2 = atomicLoad(toLoad); } diff --git a/gcc/testsuite/gdc.test/compilable/betterCinline.d b/gcc/testsuite/gdc.test/compilable/betterCinline.d new file mode 100644 index 0000000000000000000000000000000000000000..31cc8498b0da59bfae2313c570096f6a43362a95 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/betterCinline.d @@ -0,0 +1,22 @@ +/* REQUIRED_ARGS: -betterC -inline + PERMUTE_ARGS: +*/ + +struct InvBoneBindInfo +{ +} + + +struct Test(Value) +{ + void test() + { + auto t = Value.init; + } +} + +extern(C) void main() +{ + Test!(InvBoneBindInfo[32]) test; + test.test(); +} diff --git a/gcc/testsuite/gdc.test/compilable/compile1.d b/gcc/testsuite/gdc.test/compilable/compile1.d index 676108f68cfa51b6e2fc2318f16875211ab098b4..15973cf11f92cebfa6febaf7b23474a46b37d6d6 100644 --- a/gcc/testsuite/gdc.test/compilable/compile1.d +++ b/gcc/testsuite/gdc.test/compilable/compile1.d @@ -861,7 +861,7 @@ S14166 s14166; struct X14166 { this(int) { } X14166 opAssign(int) { return this; } } X14166[int] aa14166; -X14166[int] makeAA14166() { return aa14166; } +ref X14166[int] makeAA14166() { return aa14166; } struct Tup14166(T...) { T field; alias field this; } Tup14166!(int, int) tup14166; diff --git a/gcc/testsuite/gdc.test/compilable/ddoc24871.d b/gcc/testsuite/gdc.test/compilable/ddoc24871.d new file mode 100644 index 0000000000000000000000000000000000000000..8e1c227e0326f11dfd2dcde8d1a9d905872925d1 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc24871.d @@ -0,0 +1,18 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh +import std.stdio; + +/// Example +/// --- +/// void main() { +/// foreach (i; 0..10) { +/// writeln("Hello, world!"); +/// } +/// } +/// --- +void main() { + + writeln("Hello, World!"); + +} diff --git a/gcc/testsuite/gdc.test/compilable/rvalueref.d b/gcc/testsuite/gdc.test/compilable/rvalueref.d index 1c96c36f1f5b3334f0b9a363d9f506e88e7eea3e..fbef10f059fae0c5985fa2f4a91da040418d70bd 100644 --- a/gcc/testsuite/gdc.test/compilable/rvalueref.d +++ b/gcc/testsuite/gdc.test/compilable/rvalueref.d @@ -11,3 +11,9 @@ struct AS void popFront(ref string) { } static assert(!is(typeof((R r) => r.popFront))); + +// https://issues.dlang.org/show_bug.cgi?id=24883 +int toString(Writer)(ref Writer sink) => 3; +int toString(void delegate(scope const(char)[]) sink) => 4; +void put() {} +static assert(toString(dst => put()) == 4); diff --git a/gcc/testsuite/gdc.test/compilable/testVRP.d b/gcc/testsuite/gdc.test/compilable/testVRP.d index bdd72d0962b8e52644e65dc0b5a66b62d0982862..33d366d17271d026b89e5824d0a8ab519168ed36 100644 --- a/gcc/testsuite/gdc.test/compilable/testVRP.d +++ b/gcc/testsuite/gdc.test/compilable/testVRP.d @@ -4,6 +4,7 @@ // https://issues.dlang.org/show_bug.cgi?id=3147 // https://issues.dlang.org/show_bug.cgi?id=6000 // https://issues.dlang.org/show_bug.cgi?id=5225 +// https://issues.dlang.org/show_bug.cgi?id=24855 void add() { @@ -100,6 +101,10 @@ void divideFail() short w; byte y; static assert(!__traits(compiles, y = w / -1)); + static assert(!__traits(compiles, y = y / w)); + + short z; + static assert(!__traits(compiles, z = w / z + 1)); } void plus1Fail() diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19523.d b/gcc/testsuite/gdc.test/fail_compilation/b19523.d index d2a05c73dc1adfb15f48565596bbcfd2d0937169..3b2a5e0f2243194e712d65046df30df07b649421 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/b19523.d +++ b/gcc/testsuite/gdc.test/fail_compilation/b19523.d @@ -3,7 +3,7 @@ TEST_OUTPUT: ---- fail_compilation/b19523.d(13): Error: undefined identifier `SomeStruct` fail_compilation/b19523.d(14): Error: function `foo` is not callable using argument types `(_error_)` -fail_compilation/b19523.d(14): cannot pass argument `__lambda2` of type `_error_` to parameter `int delegate() arg` +fail_compilation/b19523.d(14): cannot pass argument `__lambda_L14_C6` of type `_error_` to parameter `int delegate() arg` fail_compilation/b19523.d(19): `b19523.foo(int delegate() arg)` declared here ---- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/binexperr.d b/gcc/testsuite/gdc.test/fail_compilation/binexperr.d new file mode 100644 index 0000000000000000000000000000000000000000..695b2746b186674efd45d479209923411e788625 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/binexperr.d @@ -0,0 +1,14 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/binexperr.d(12): Error: expression expected, not `)` +fail_compilation/binexperr.d(12): Error: missing closing `)` after `if (A1 * (__error)` +--- +*/ + +void main() +{ + struct A1 {} + if (A1*) {} + return; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d b/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d new file mode 100644 index 0000000000000000000000000000000000000000..2665d69cc98e7608d5d6021e936674755d5f5e10 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d @@ -0,0 +1,25 @@ +/* REQUIRED_ARGS: -preview=bitfields + * TEST_OUTPUT: +--- +fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S` +fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i` +--- +*/ + +struct S +{ + int a,b; + int :2, c:3; +} + +static assert(__traits(getBitfieldOffset, S.b) == 0); +static assert(__traits(getBitfieldWidth, S.b) == 32); +static assert(__traits(getBitfieldOffset, S.c) == 2); +static assert(__traits(getBitfieldWidth, S.c) == 3); + +void test0() +{ + int i; + i = __traits(getBitfieldOffset, S); + i = __traits(getBitfieldOffset, i); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d index 02fc7db06183e8debd7b4c22cef4d806cdc442e4..60828bef17a7b6234fc30bd59ad5d10d74203579 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d +++ b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d @@ -65,7 +65,7 @@ TEST_OUTPUT: fail_compilation/bug9631.d(80): Error: function `f` is not callable using argument types `(int, S)` fail_compilation/bug9631.d(80): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s` fail_compilation/bug9631.d(79): `bug9631.arg.f(int i, S s)` declared here -fail_compilation/bug9631.d(81): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(81): Error: function literal `__lambda_L81_C5(S s)` is not callable using argument types `(S)` fail_compilation/bug9631.d(81): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s` fail_compilation/bug9631.d(87): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)` fail_compilation/bug9631.d(87): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0` diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d index 7515208643711fb29ad8914ad27acae6f9dce0ee..b3a335b4e242e0ec6bec0715fcfdca7e8de65f1f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d +++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d @@ -5,7 +5,7 @@ TEST_OUTPUT: fail_compilation/constraints_defs.d(49): Error: template instance `constraints_defs.main.def!(int, 0, (a) => a)` does not match template declaration `def(T, int i = 5, alias R)()` with `T = int, i = 0, - R = __lambda1` + R = __lambda_L49_C18` must satisfy the following constraint: ` N!T` fail_compilation/constraints_defs.d(50): Error: template instance `imports.constraints.defa!int` does not match template declaration `defa(T, U = int)()` diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d index 06caa52493d2e2285467545bcb424e8a300a13fc..fee7a3e3e7b1ad425714cc7959d28a5366578ee4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d +++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d @@ -22,7 +22,7 @@ fail_compilation/constraints_tmpl.d(41): Error: template instance `imports.const ` N!T N!U` fail_compilation/constraints_tmpl.d(43): Error: template instance `constraints_tmpl.main.lambda!((a) => a)` does not match template declaration `lambda(alias pred)()` - with `pred = __lambda1` + with `pred = __lambda_L43_C13` must satisfy the following constraint: ` N!int` --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/cppvar.d b/gcc/testsuite/gdc.test/fail_compilation/cppvar.d index 885a55547c9bd3ca3fed34f81984fabc7abbe02f..213b54d10f56a797659223a42e5f94bbd1d58031 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/cppvar.d +++ b/gcc/testsuite/gdc.test/fail_compilation/cppvar.d @@ -9,7 +9,7 @@ fail_compilation/cppvar.d(21): Error: variable `cppvar.staticVar` cannot have `e fail_compilation/cppvar.d(21): perhaps declare it as `__gshared` instead fail_compilation/cppvar.d(22): Error: variable `cppvar.sharedVar` cannot have `extern(C++)` linkage because it is `shared` fail_compilation/cppvar.d(22): perhaps declare it as `__gshared` instead -fail_compilation/cppvar.d(30): Error: delegate `cppvar.__lambda7` cannot return type `bool[3]` because its linkage is `extern(C++)` +fail_compilation/cppvar.d(30): Error: delegate `cppvar.__lambda_L30_C46` cannot return type `bool[3]` because its linkage is `extern(C++)` --- */ #line 10 diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12829.d b/gcc/testsuite/gdc.test/fail_compilation/diag12829.d index aaedd0f72175a06ba085c37ce0dccead62502ae3..0ed04d3a7495f1ab40e9bd43b25a96b21a5fdf34 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag12829.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12829.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/diag12829.d(15): Error: function `diag12829.test1` is `@nogc` yet allocates closure for `test1()` with the GC -fail_compilation/diag12829.d(18): delegate `diag12829.test1.__lambda2` closes over variable `x` +fail_compilation/diag12829.d(18): delegate `diag12829.test1.__lambda_L18_C33` closes over variable `x` fail_compilation/diag12829.d(17): `x` declared here fail_compilation/diag12829.d(22): function `diag12829.test1.bar` closes over variable `x` fail_compilation/diag12829.d(17): `x` declared here diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag15411.d b/gcc/testsuite/gdc.test/fail_compilation/diag15411.d index 8b18c19d0276f58fa3d7ff8438356bb6511b976e..51060b8ffc158020f44eda3077e86911557fbb4e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag15411.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag15411.d @@ -2,9 +2,9 @@ /* TEST_OUTPUT: --- -fail_compilation/diag15411.d(17): Error: function `diag15411.test15411.__funcliteral2` cannot access variable `i` in frame of function `diag15411.test15411` +fail_compilation/diag15411.d(17): Error: function `diag15411.test15411.__funcliteral_L17_C15` cannot access variable `i` in frame of function `diag15411.test15411` fail_compilation/diag15411.d(16): `i` declared here -fail_compilation/diag15411.d(18): Error: function `diag15411.test15411.__funcliteral4` cannot access variable `i` in frame of function `diag15411.test15411` +fail_compilation/diag15411.d(18): Error: function `diag15411.test15411.__funcliteral_L18_C15` cannot access variable `i` in frame of function `diag15411.test15411` fail_compilation/diag15411.d(16): `i` declared here fail_compilation/diag15411.d(26): Error: `static` function `diag15411.testNestedFunction.myFunc2` cannot access function `myFunc1` in frame of function `diag15411.testNestedFunction` fail_compilation/diag15411.d(25): `myFunc1` declared here diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag20268.d b/gcc/testsuite/gdc.test/fail_compilation/diag20268.d index 0653490267005f5440a8fe1db36406409007055c..5504aad0c7834d1c7e09f14838e03c045241ee7c 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag20268.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag20268.d @@ -3,8 +3,8 @@ /* TEST_OUTPUT: --- -fail_compilation/diag20268.d(12): Error: template `__lambda4` is not callable using argument types `!()(int)` -fail_compilation/diag20268.d(11): Candidate is: `__lambda4(__T1, __T2)(x, y)` +fail_compilation/diag20268.d(12): Error: template `__lambda_L11_C1` is not callable using argument types `!()(int)` +fail_compilation/diag20268.d(11): Candidate is: `__lambda_L11_C1(__T1, __T2)(x, y)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag24812.d b/gcc/testsuite/gdc.test/fail_compilation/diag24812.d new file mode 100644 index 0000000000000000000000000000000000000000..626f47ebf010f7708fd403e3913971057c9397fa --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag24812.d @@ -0,0 +1,7 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag24812.d(7): Error: enum `diag24812.Foo` enum `Foo` must have at least one member +--- +*/ +enum Foo {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9831.d b/gcc/testsuite/gdc.test/fail_compilation/diag9831.d index c93a06a465ac85fe921d09d60276b344e8c98e8f..13995d4b6ad1216f5a50faa7470ed8512cb3b8b5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag9831.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag9831.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3(__T1)(x)` cannot access variable `c` in frame of function `D main` +fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda_L13_C12(__T1)(x)` cannot access variable `c` in frame of function `D main` fail_compilation/diag9831.d(11): `c` declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d b/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d index f00f91a1fc81c3fc0799d01576a1c0112689411f..0173d4b78eff7dd88f1953e754cf8761c9692f52 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d @@ -1,19 +1,19 @@ /** TEST_OUTPUT: --- -fail_compilation/diag_funclit.d(103): Error: function literal `__lambda1(x, y, z)` is not callable using argument types `()` +fail_compilation/diag_funclit.d(103): Error: function literal `__lambda_L103_C5(x, y, z)` is not callable using argument types `()` fail_compilation/diag_funclit.d(103): too few arguments, expected 3, got 0 -fail_compilation/diag_funclit.d(106): Error: function literal `__lambda2(x, y, z)` is not callable using argument types `(int, string, int, int)` +fail_compilation/diag_funclit.d(106): Error: function literal `__lambda_L106_C5(x, y, z)` is not callable using argument types `(int, string, int, int)` fail_compilation/diag_funclit.d(106): too many arguments, expected 3, got 4 -fail_compilation/diag_funclit.d(108): Error: function literal `__lambda3(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)` +fail_compilation/diag_funclit.d(108): Error: function literal `__lambda_L108_C5(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)` fail_compilation/diag_funclit.d(108): too many arguments, expected 3, got 4 -fail_compilation/diag_funclit.d(110): Error: function literal `__lambda4(x, y, string z = "Hello")` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(110): Error: function literal `__lambda_L110_C5(x, y, string z = "Hello")` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(110): too few arguments, expected 3, got 1 -fail_compilation/diag_funclit.d(112): Error: function literal `__lambda5(x, y, z)` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(112): Error: function literal `__lambda_L112_C5(x, y, z)` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(112): too few arguments, expected 3, got 1 -fail_compilation/diag_funclit.d(115): Error: function literal `__lambda6(x, y, ...)` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(115): Error: function literal `__lambda_L115_C5(x, y, ...)` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(115): too few arguments, expected 2, got 1 -fail_compilation/diag_funclit.d(117): Error: function literal `__lambda7(x, y, string z = "Hey", ...)` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(117): Error: function literal `__lambda_L117_C5(x, y, string z = "Hey", ...)` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(117): too few arguments, expected 3, got 1 --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d index 0482e87a5492801df5d9c6fc9e2c74c51d9a79c6..100c5e1ed5a9ea5ddcb52a12b9cdf6fda37582dd 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d +++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d @@ -16,7 +16,7 @@ fail_compilation/e15876_3.d(29): Error: found `End of File` when expecting `)` fail_compilation/e15876_3.d(29): Error: no identifier for declarator `d(_error_ = () { for (__error__ - 0; 0) + __error; __error) { __error__ } diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d index e5c3bbf5673313d235e56ac274fcae6e74de6789..a9115b92dd54d9af66496e405a37cbe3a831f8b0 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d +++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d @@ -15,7 +15,7 @@ fail_compilation/e15876_4.d(27): Error: found `End of File` when expecting `)` fail_compilation/e15876_4.d(27): Error: no identifier for declarator `typeof(() { for (__error__ - 0; 0) + __error; __error) { __error__ } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11125.d b/gcc/testsuite/gdc.test/fail_compilation/fail11125.d index 1a682cd197c834a079207fb64107e6638ecee48c..4349755519d17fa109ff312d87d6acf98f35de7b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail11125.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11125.d @@ -2,11 +2,11 @@ TEST_OUTPUT: --- fail_compilation/fail11125.d(26): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)` - with `predfun = __lambda1` + with `predfun = __lambda_L26_C13` must satisfy the following constraint: ` is(ReturnType!predfun == bool)` fail_compilation/fail11125.d(27): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)` - with `predfun = __lambda2` + with `predfun = __lambda_L27_C17` must satisfy the following constraint: ` is(ReturnType!predfun == bool)` --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12236.d b/gcc/testsuite/gdc.test/fail_compilation/fail12236.d index 824f5e48db2590d4dd305bf97c3a74b4f47f7adc..f27cc2a52159b870a2cf86b1b047382249a91a30 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail12236.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12236.d @@ -6,8 +6,8 @@ fail_compilation/fail12236.d(16): while evaluating `pragma(msg, f1.mangle fail_compilation/fail12236.d(21): Error: forward reference to inferred return type of function `f2` fail_compilation/fail12236.d(21): while evaluating `pragma(msg, f2(T)(T).mangleof)` fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating -fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1` -fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1(__T1)(a).mangleof)` +fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda_L29_C5` +fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda_L29_C5(__T1)(a).mangleof)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12378.d b/gcc/testsuite/gdc.test/fail_compilation/fail12378.d index 77678ebc0fc08e757f224734ba0f15eb054b6241..5a0f9e05ab66d690b9b10cb0eb71639420e98a68 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail12378.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12378.d @@ -5,7 +5,7 @@ fail_compilation/fail12378.d(18): Error: undefined identifier `ANYTHING` fail_compilation/fail12378.d(18): Error: undefined identifier `GOES` fail_compilation/fail12378.d(91): instantiated from here: `MapResultS!((x0) => ANYTHING - GOES, Result)` fail_compilation/fail12378.d(17): instantiated from here: `mapS!(Result)` -fail_compilation/fail12378.d(100): instantiated from here: `__lambda1!int` +fail_compilation/fail12378.d(100): instantiated from here: `__lambda_L16_C19!int` fail_compilation/fail12378.d(91): instantiated from here: `MapResultS!((y0) => iota(2).mapS!((x0) => ANYTHING - GOES), Result)` fail_compilation/fail12378.d(16): instantiated from here: `mapS!(Result)` --- @@ -27,7 +27,7 @@ fail_compilation/fail12378.d(40): Error: undefined identifier `ANYTHING` fail_compilation/fail12378.d(40): Error: undefined identifier `GOES` fail_compilation/fail12378.d(112): instantiated from here: `MapResultC!((x0) => ANYTHING - GOES, Result)` fail_compilation/fail12378.d(39): instantiated from here: `mapC!(Result)` -fail_compilation/fail12378.d(123): instantiated from here: `__lambda1!int` +fail_compilation/fail12378.d(123): instantiated from here: `__lambda_L38_C19!int` fail_compilation/fail12378.d(112): instantiated from here: `MapResultC!((y0) => iota(2).mapC!((x0) => ANYTHING - GOES), Result)` fail_compilation/fail12378.d(38): instantiated from here: `mapC!(Result)` --- @@ -49,7 +49,7 @@ fail_compilation/fail12378.d(64): Error: undefined identifier `ANYTHING` fail_compilation/fail12378.d(64): Error: undefined identifier `GOES` fail_compilation/fail12378.d(135): instantiated from here: `MapResultI!((x0) => ANYTHING - GOES, Result)` fail_compilation/fail12378.d(63): instantiated from here: `mapI!(Result)` -fail_compilation/fail12378.d(143): instantiated from here: `__lambda1!int` +fail_compilation/fail12378.d(143): instantiated from here: `__lambda_L62_C19!int` fail_compilation/fail12378.d(135): instantiated from here: `MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result)` fail_compilation/fail12378.d(62): instantiated from here: `mapI!(Result)` --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12908.d b/gcc/testsuite/gdc.test/fail_compilation/fail12908.d index 67ea6cefb22747862de49b0e9253652c62a1e9f8..3cc6306a80bb896e8edee6852074483c5c3b4e43 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail12908.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12908.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail12908.d(14): Error: `pure` delegate `fail12908.main.__foreachbody1` cannot call impure function `fail12908.g` +fail_compilation/fail12908.d(14): Error: `pure` delegate `fail12908.main.__foreachbody_L12_C5` cannot call impure function `fail12908.g` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13120.d b/gcc/testsuite/gdc.test/fail_compilation/fail13120.d index 6a1335eebd5b006ee8ad6345e31a018c19dc095a..1d1127c4c305b12f089be5afd1d07783b4322d53 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13120.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13120.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail13120.d(13): Error: `pure` delegate `fail13120.g1.__foreachbody2` cannot call impure function `fail13120.f1` -fail_compilation/fail13120.d(13): Error: `@nogc` delegate `fail13120.g1.__foreachbody2` cannot call non-@nogc function `fail13120.f1` +fail_compilation/fail13120.d(13): Error: `pure` delegate `fail13120.g1.__foreachbody_L12_C5` cannot call impure function `fail13120.f1` +fail_compilation/fail13120.d(13): Error: `@nogc` delegate `fail13120.g1.__foreachbody_L12_C5` cannot call non-@nogc function `fail13120.f1` --- */ void f1() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13424.d b/gcc/testsuite/gdc.test/fail_compilation/fail13424.d index dcec523a0133588373f646fee0d51045a0fb85d6..1a7f16b55c5599c8663c91c6225b18afb38d0bb0 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13424.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13424.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail13424.d(12): Error: delegate `fail13424.S.__lambda2` cannot be struct members -fail_compilation/fail13424.d(17): Error: delegate `fail13424.U.__lambda2` cannot be union members -fail_compilation/fail13424.d(22): Error: delegate `fail13424.C.__lambda2` cannot be class members +fail_compilation/fail13424.d(12): Error: delegate `fail13424.S.__lambda_L12_C35` cannot be struct members +fail_compilation/fail13424.d(17): Error: delegate `fail13424.U.__lambda_L17_C35` cannot be union members +fail_compilation/fail13424.d(22): Error: delegate `fail13424.C.__lambda_L22_C35` cannot be class members --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17969.d b/gcc/testsuite/gdc.test/fail_compilation/fail17969.d index 29bc3f448d8364238bc93ad0fc7960bedec1390e..8272fb0b18c30028c298017426a59e5e6e1f3614 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail17969.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail17969.d @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17969.d(10): Error: no property `sum` for type `fail17969.__lambda6!(int[]).__lambda6.MapResult2!((b) => b)` +fail_compilation/fail17969.d(10): Error: no property `sum` for type `fail17969.__lambda_L10_C1!(int[]).__lambda_L10_C1.MapResult2!((b) => b)` fail_compilation/fail17969.d(16): struct `MapResult2` defined here --- * https://issues.dlang.org/show_bug.cgi?id=17969 diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail39.d b/gcc/testsuite/gdc.test/fail_compilation/fail39.d index c0bb0e16a076042f2856b75b577cbc1fa5aa3826..f740dda98f16744ff6da3e9fe9b3bd097de34f8c 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail39.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail39.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail39.d(12): Error: function `fail39.main.__funcliteral2` cannot access function `foo` in frame of function `D main` +fail_compilation/fail39.d(12): Error: function `fail39.main.__funcliteral_L12_C27` cannot access function `foo` in frame of function `D main` fail_compilation/fail39.d(11): `foo` declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6795.d b/gcc/testsuite/gdc.test/fail_compilation/fail6795.d index 26d44296498801414682b78222ef778e90fc3d8a..dbdd5d5666b5eba1bb22f0f167c0adcb0ae7d77e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6795.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6795.d @@ -8,9 +8,9 @@ fail_compilation/fail6795.d(22): Error: cannot modify expression `[0][0]` becaus fail_compilation/fail6795.d(23): Error: cannot modify expression `[0:0][0]` because it is not an lvalue fail_compilation/fail6795.d(25): Error: cannot take address of expression `[0][0]` because it is not an lvalue fail_compilation/fail6795.d(26): Error: cannot take address of expression `[0:0][0]` because it is not an lvalue +fail_compilation/fail6795.d(30): Error: cannot modify expression `Some["zz"]` because it is not an lvalue --- */ - void test_wrong_line_num() { enum int[1] sa = [0]; @@ -24,4 +24,8 @@ void test_wrong_line_num() auto ps = &sa[0]; auto pa = &aa[0]; + + // https://issues.dlang.org/show_bug.cgi?id=24845 + enum Maps : int[string] { Some = ["aa" : 12], Other = ["bb" : 24] } + Maps.Some["zz"] = 44; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/hexstring.d b/gcc/testsuite/gdc.test/fail_compilation/hexstring.d index 0f23f444389d901191fec1ac3759c11828f44c93..edbb4e67bc0daf0f08cf608f5d920cc9391423ab 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/hexstring.d +++ b/gcc/testsuite/gdc.test/fail_compilation/hexstring.d @@ -14,6 +14,7 @@ fail_compilation/hexstring.d(39): perhaps remove postfix `c` from hex str fail_compilation/hexstring.d(40): Error: hex string with `dstring` type needs to be multiple of 4 bytes, not 5 fail_compilation/hexstring.d(41): Error: cannot implicitly convert expression `x"11223344"d` of type `dstring` to `immutable(float[])` fail_compilation/hexstring.d(42): Error: cannot implicitly convert expression `x"1122"w` of type `wstring` to `immutable(ubyte[])` +fail_compilation/hexstring.d(50): Error: array cast from `string` to `S[]` is not supported at compile time fail_compilation/hexstring.d(28): Error: cannot implicitly convert expression `x"123F"` of type `string` to `ubyte[]` --- */ @@ -21,7 +22,6 @@ immutable ubyte[] s0 = x"123F"; static assert(s0[0] == 0x12); static assert(s0[1] == 0x3F); immutable byte[] s1 = x"123F"; - enum E(X) = cast(X[]) x"AABBCCDD"; static assert(E!int[0] == 0xAABBCCDD); @@ -40,3 +40,11 @@ immutable uint[] f11 = cast(immutable uint[]) x"AABBCCDD"c; immutable uint[] f12 = x"1122334455"d; immutable float[] f13 = x"11223344"d; immutable ubyte[] f14 = x"1122"w; + +// https://issues.dlang.org/show_bug.cgi?id=24832 +struct S +{ + ushort l0, l1, l2, l3, l4, l5; +} + +immutable S[] returnValues = cast(S[]) x"FFFFFFFFFFFFFFFFFFFFFFFF"; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d index 552a9824f32e6a892d1e6c5187184a8d48394e36..fc6c593ae0a638049a18bc2d5b44cf0f049ef6ab 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10922.d(11): Error: function `__lambda4` is not callable using argument types `()` +fail_compilation/ice10922.d(11): Error: function `__lambda_L10_C12` is not callable using argument types `()` fail_compilation/ice10922.d(11): too few arguments, expected 1, got 0 -fail_compilation/ice10922.d(10): `ice10922.__lambda4(in uint n)` declared here +fail_compilation/ice10922.d(10): `ice10922.__lambda_L10_C12(in uint n)` declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11822.d b/gcc/testsuite/gdc.test/fail_compilation/ice11822.d index 830af212cb7add9261debf3343367e0f17907b78..a673a6b2dda6042eb878ca0aabdd66ea416c73bb 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11822.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11822.d @@ -5,8 +5,8 @@ TEST_OUTPUT: --- fail_compilation/ice11822.d(33): Deprecation: function `ice11822.d` is deprecated -fail_compilation/ice11822.d(16): instantiated from here: `__lambda2!int` -fail_compilation/ice11822.d(22): instantiated from here: `S!(__lambda2)` +fail_compilation/ice11822.d(16): instantiated from here: `__lambda_L33_C15!int` +fail_compilation/ice11822.d(22): instantiated from here: `S!(__lambda_L33_C15)` fail_compilation/ice11822.d(33): instantiated from here: `g!((n) => d(i))` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11850.d b/gcc/testsuite/gdc.test/fail_compilation/ice11850.d index d33549a27f2ba79d1b6dd709b7a4275d68035cb1..510fe0aae82dcc9ba23bb4828ea3de6601a9f614 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11850.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11850.d @@ -3,7 +3,7 @@ EXTRA_FILES: imports/a11850.d TEST_OUTPUT: --- fail_compilation/ice11850.d(15): Error: incompatible types for `(a) < ([0])`: `uint[]` and `int[]` -fail_compilation/imports/a11850.d(9): instantiated from here: `FilterResult!(__lambda1, uint[][])` +fail_compilation/imports/a11850.d(9): instantiated from here: `FilterResult!(__lambda_L15_C13, uint[][])` fail_compilation/ice11850.d(15): instantiated from here: `filter!(uint[][])` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12235.d b/gcc/testsuite/gdc.test/fail_compilation/ice12235.d index 8f2fefd099efb45d8f085e18ff000e6035655d26..ca453e0928d0774ec3a850cefef916f481be5bc6 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice12235.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12235.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice12235.d(14): Error: forward reference to inferred return type of function `__lambda1` -fail_compilation/ice12235.d(15): Error: forward reference to inferred return type of function `__lambda1` -fail_compilation/ice12235.d(15): while evaluating `pragma(msg, __lambda1.mangleof)` +fail_compilation/ice12235.d(14): Error: forward reference to inferred return type of function `__lambda_L12_C5` +fail_compilation/ice12235.d(15): Error: forward reference to inferred return type of function `__lambda_L12_C5` +fail_compilation/ice12235.d(15): while evaluating `pragma(msg, __lambda_L12_C5.mangleof)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15855.d b/gcc/testsuite/gdc.test/fail_compilation/ice15855.d index f7ad3900683f05d202a6e96a7d9433c69356ebbc..5d1cea1de72bd5bf944dc7fc0002070dc0bf9b60 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice15855.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice15855.d @@ -15,7 +15,7 @@ fail_compilation/ice15855.d(28): Error: found `End of File` when expecting `]` fail_compilation/ice15855.d(28): Error: no identifier for declarator `a[() { for (__error__ - 0; 0) + __error; __error) { __error__ } diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8309.d b/gcc/testsuite/gdc.test/fail_compilation/ice8309.d index 2446914477a177077658cddc7d7f3ef1f3a2940e..a9f6f7253acbf6e52e6d8e104156584c868c92c1 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice8309.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice8309.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice8309.d(10): Error: incompatible types for `(__lambda1) : (__lambda2)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe` +fail_compilation/ice8309.d(10): Error: incompatible types for `(__lambda_L10_C15) : (__lambda_L10_C24)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8795.d b/gcc/testsuite/gdc.test/fail_compilation/ice8795.d index a30a65b200b75ca1b296fee469d8a7c5486d16ab..33aa6e4d54b4b464f900dffcdf43ba252736a3b3 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice8795.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice8795.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `(` fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `End of File` -fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (0` +fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (__error` fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` instead of statement fail_compilation/ice8795.d-mixin-15(15): Error: { } expected following `interface` declaration fail_compilation/ice8795.d-mixin-15(15): Error: anonymous interfaces not allowed diff --git a/gcc/testsuite/gdc.test/fail_compilation/misc1.d b/gcc/testsuite/gdc.test/fail_compilation/misc1.d index 95bcc6d03d47db704f0752b69cfed7596660ebe0..90e42d83e9911a0c43aadf3ef1e2362a53a52c5e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/misc1.d +++ b/gcc/testsuite/gdc.test/fail_compilation/misc1.d @@ -5,7 +5,7 @@ fail_compilation/misc1.d(109): Error: `5` has no effect fail_compilation/misc1.d(110): Error: `1 + 2` has no effect fail_compilation/misc1.d(111): Error: `x` has no effect fail_compilation/misc1.d(117): Deprecation: `1 * 1` has no effect -fail_compilation/misc1.d(118): Deprecation: `__lambda3` has no effect +fail_compilation/misc1.d(118): Deprecation: `__lambda_L118_C34` has no effect fail_compilation/misc1.d(124): Deprecation: `false` has no effect fail_compilation/misc1.d(127): Deprecation: `*sp++` has no effect fail_compilation/misc1.d(128): Deprecation: `j` has no effect diff --git a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d index a170b77b88c2fd6e78a584d98b9cc3f324b54a06..4a8875d6d69cde16ef0baf69982063ab07c23929 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d +++ b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d @@ -24,7 +24,7 @@ fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected fail_compilation/misc_parser_err_cov1.d(41): Error: identifier or new keyword expected following `(...)`. fail_compilation/misc_parser_err_cov1.d(41): Error: expression expected, not `;` fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following expression -fail_compilation/misc_parser_err_cov1.d(41): expression: `(__error) + 0` +fail_compilation/misc_parser_err_cov1.d(41): expression: `(__error) + (__error)` fail_compilation/misc_parser_err_cov1.d(43): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/misc_parser_err_cov1.d(33): unmatched `{` --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/opapplyscope.d b/gcc/testsuite/gdc.test/fail_compilation/opapplyscope.d index 8414bf12845481547afb22c0cb2d5e58edc47cc9..3eef0f92937210c980f82c9ceba12d18b430e148 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/opapplyscope.d +++ b/gcc/testsuite/gdc.test/fail_compilation/opapplyscope.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- fail_compilation/opapplyscope.d(113): Error: function `opapplyscope.S.opApply(scope int delegate(scope int* ptr) @safe dg)` is not callable using argument types `(int delegate(int* x) nothrow @nogc @safe)` -fail_compilation/opapplyscope.d(113): cannot pass argument `__foreachbody3` of type `int delegate(int* x) nothrow @nogc @safe` to parameter `scope int delegate(scope int* ptr) @safe dg` +fail_compilation/opapplyscope.d(113): cannot pass argument `__foreachbody_L113_C5` of type `int delegate(int* x) nothrow @nogc @safe` to parameter `scope int delegate(scope int* ptr) @safe dg` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/previewin.d b/gcc/testsuite/gdc.test/fail_compilation/previewin.d index 486dcd5244d28da01bf35f1fd7b670417e796e27..c3d11cbfd0b9f2a7f81bc891501ae611678f9de3 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/previewin.d +++ b/gcc/testsuite/gdc.test/fail_compilation/previewin.d @@ -3,13 +3,13 @@ REQUIRED_ARGS: -preview=in -preview=dip1000 TEST_OUTPUT: ---- fail_compilation/previewin.d(4): Error: function `takeFunction` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(4): cannot pass argument `__lambda_L4_C18` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here fail_compilation/previewin.d(5): Error: function `takeFunction` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(5): cannot pass argument `__lambda_L5_C18` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here fail_compilation/previewin.d(6): Error: function `takeFunction` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(6): cannot pass argument `__lambda_L6_C18` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal` diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope.d b/gcc/testsuite/gdc.test/fail_compilation/retscope.d index 919d9401cd5f960d1b72ef579edd1f9c2f665dff..acad99bdaf6c9086183ce5aa29cf5811d6ce1a5a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope.d @@ -55,7 +55,7 @@ void test2(scope int* p, int[] a ...) @safe TEST_OUTPUT: --- fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closure for `onReceive()` with the GC -fail_compilation/retscope.d(77): delegate `retscope.HTTP.Impl.onReceive.__lambda1` closes over variable `this` +fail_compilation/retscope.d(77): delegate `retscope.HTTP.Impl.onReceive.__lambda_L77_C23` closes over variable `this` --- */ @@ -234,10 +234,10 @@ void* funretscope(scope dg_t ptr) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` -fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` -fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` -fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` +fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda_L248_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` +fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda_L248_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` +fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda_L249_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` +fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda_L249_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/safer.d b/gcc/testsuite/gdc.test/fail_compilation/safer.d new file mode 100644 index 0000000000000000000000000000000000000000..a7f260a2848930ba9be89141ac648237e9d866d4 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/safer.d @@ -0,0 +1,18 @@ +/* REQUIRED_ARGS: -preview=safer +TEST_OUTPUT: +--- +fail_compilation/safer.d(10): Error: `void` initializers for pointers not allowed in safe functions +--- +*/ + +void test1() +{ + int* p = void; +} + +void foo3() { } + +void test2() +{ + foo3(); // should not be an error +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15306.d b/gcc/testsuite/gdc.test/fail_compilation/test15306.d index ff532aea220aba820883d88a99ef8146a5d19842..a60e27404215092eb5de79ad34f6b801daa5de46 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test15306.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test15306.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/test15306.d(15): Error: `immutable` delegate `test15306.main.__dgliteral2` cannot access mutable data `i` -fail_compilation/test15306.d(19): Error: `shared` delegate `test15306.main.__dgliteral5` cannot access non-shared data `p` +fail_compilation/test15306.d(15): Error: `immutable` delegate `test15306.main.__dgliteral_L15_C16` cannot access mutable data `i` +fail_compilation/test15306.d(19): Error: `shared` delegate `test15306.main.__dgliteral_L19_C16` cannot access non-shared data `p` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16193.d b/gcc/testsuite/gdc.test/fail_compilation/test16193.d index 39399cf01d8056b0a9f282b476ace0e233c7d075..84dc7d1c6d75e7d42b1fa1cdb08989a1bda659b4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test16193.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test16193.d @@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- fail_compilation/test16193.d(39): Error: function `test16193.abc` is `@nogc` yet allocates closure for `abc()` with the GC -fail_compilation/test16193.d(41): delegate `test16193.abc.__foreachbody2` closes over variable `x` +fail_compilation/test16193.d(41): delegate `test16193.abc.__foreachbody_L41_C5` closes over variable `x` fail_compilation/test16193.d(40): `x` declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17451.d b/gcc/testsuite/gdc.test/fail_compilation/test17451.d index b0cda2105a22aeff6230696b8dc365db60d8d9a7..7df77f591f304b037c4ddf52ccdec462a15b7fd7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test17451.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test17451.d @@ -2,7 +2,7 @@ --- fail_compilation/test17451.d(22): Error: undefined identifier `allocator` fail_compilation/test17451.d(23): Error: `false` has no effect -fail_compilation/test17451.d(30): Error: variable `test17451.HashMap!(ThreadSlot).HashMap.__lambda2.v` - size of type `ThreadSlot` is invalid +fail_compilation/test17451.d(30): Error: variable `test17451.HashMap!(ThreadSlot).HashMap.__lambda_L30_C20.v` - size of type `ThreadSlot` is invalid fail_compilation/test17451.d(44): Error: template instance `test17451.HashMap!(ThreadSlot)` error instantiating --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19107.d b/gcc/testsuite/gdc.test/fail_compilation/test19107.d index 9a6e335864779fbb07b52035c10e27a5ed37924b..8bbfa82717f65c902a69379d7f1cc2d49584d8c5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test19107.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test19107.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/test19107.d(24): Error: template `all` is not callable using argument types `!((c) => c)(string[])` fail_compilation/test19107.d(18): Candidate is: `all(alias pred, T)(T t)` - with `pred = __lambda2, + with `pred = __lambda_L24_C15, T = string[]` must satisfy the following constraint: ` is(typeof(I!pred(t)))` diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19971.d b/gcc/testsuite/gdc.test/fail_compilation/test19971.d index 3d46eeb82ef07feb8beb80a45a97616b74e7dc75..b99afddbdd50ee94db786ee0afb75e53f5a0c1bb 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test19971.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test19971.d @@ -3,7 +3,7 @@ fail_compilation/test19971.d(16): Error: function `f` is not callable using argument types `(string)` fail_compilation/test19971.d(16): cannot pass argument `"%s"` of type `string` to parameter `int x` fail_compilation/test19971.d(13): `test19971.f(int x)` declared here -fail_compilation/test19971.d(17): Error: function literal `__lambda1(int x)` is not callable using argument types `(string)` +fail_compilation/test19971.d(17): Error: function literal `__lambda_L17_C5(int x)` is not callable using argument types `(string)` fail_compilation/test19971.d(17): cannot pass argument `"%s"` of type `string` to parameter `int x` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20719.d b/gcc/testsuite/gdc.test/fail_compilation/test20719.d index b9305f20f80fac4ff7311e1d0c33053cf0249a8b..4db59d62ae1f4baa1a7782003d93ea0f6a01eac9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test20719.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test20719.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference -fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda2.foo` - size of type `SumType` is invalid +fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda_L32_C22.foo` - size of type `SumType` is invalid fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21912.d b/gcc/testsuite/gdc.test/fail_compilation/test21912.d index f8bcb40b5dfbe058368c7eaecbe1b5d8ab9adcdb..7e236c8a33a226b7f2f69c61669da266f380fbe7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test21912.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test21912.d @@ -3,16 +3,16 @@ PERMUTE_ARGS: -preview=dip1000 TEST_OUTPUT: --- fail_compilation/test21912.d(28): Error: function `test21912.escapeParam` is `@nogc` yet allocates closure for `escapeParam()` with the GC -fail_compilation/test21912.d(30): delegate `test21912.escapeParam.__lambda2` closes over variable `i` +fail_compilation/test21912.d(30): delegate `test21912.escapeParam.__lambda_L30_C21` closes over variable `i` fail_compilation/test21912.d(28): `i` declared here fail_compilation/test21912.d(33): Error: function `test21912.escapeAssign` is `@nogc` yet allocates closure for `escapeAssign()` with the GC -fail_compilation/test21912.d(35): delegate `test21912.escapeAssign.__lambda3` closes over variable `i` +fail_compilation/test21912.d(35): delegate `test21912.escapeAssign.__lambda_L35_C10` closes over variable `i` fail_compilation/test21912.d(33): `i` declared here fail_compilation/test21912.d(44): Error: function `test21912.escapeAssignRef` is `@nogc` yet allocates closure for `escapeAssignRef()` with the GC -fail_compilation/test21912.d(46): delegate `test21912.escapeAssignRef.__lambda3` closes over variable `i` +fail_compilation/test21912.d(46): delegate `test21912.escapeAssignRef.__lambda_L46_C10` closes over variable `i` fail_compilation/test21912.d(44): `i` declared here fail_compilation/test21912.d(55): Error: function `test21912.escapeParamInferred` is `@nogc` yet allocates closure for `escapeParamInferred()` with the GC -fail_compilation/test21912.d(57): delegate `test21912.escapeParamInferred.__lambda2` closes over variable `i` +fail_compilation/test21912.d(57): delegate `test21912.escapeParamInferred.__lambda_L57_C29` closes over variable `i` fail_compilation/test21912.d(55): `i` declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23170.d b/gcc/testsuite/gdc.test/fail_compilation/test23170.d index eb79cd81565eda2bb9802e0e1e1d5c9a8af45a33..fedf31b5fc838364797dc3bf664dd6d744eb3554 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test23170.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test23170.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test23170.d(10): Error: array literal in `@nogc` delegate `test23170.__lambda5` may cause a GC allocation +fail_compilation/test23170.d(10): Error: array literal in `@nogc` delegate `test23170.__lambda_L10_C15` may cause a GC allocation --- */ // https://issues.dlang.org/show_bug.cgi?id=23170 diff --git a/gcc/testsuite/gdc.test/fail_compilation/test24353.d b/gcc/testsuite/gdc.test/fail_compilation/test24353.d new file mode 100644 index 0000000000000000000000000000000000000000..76174aee62dda9639cf9caa0c66c0bb296571554 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test24353.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=24353 + +/** +TEST_OUTPUT: +--- +fail_compilation/test24353.d(23): Error: mutable method `test24353.S.opApply` is not callable using a `const` object +fail_compilation/test24353.d(14): Consider adding `const` or `inout` here +--- +*/ + + +struct S +{ + int opApply(int delegate(int) dg) + { + return 0; + } +} + +void example() +{ + const S s; + foreach (e; s) {} // Error expected here +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test24745.d b/gcc/testsuite/gdc.test/fail_compilation/test24745.d new file mode 100644 index 0000000000000000000000000000000000000000..6d9c335bb4af3f30752506d6c06563fb3225d725 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test24745.d @@ -0,0 +1,13 @@ +// https://issues.dlang.org/show_bug.cgi?id=24745 + +/* +TEST_OUTPUT: +--- +fail_compilation/test24745.d(12): Error: incorrect syntax for associative array, expected `[]`, found `{}` +--- +*/ + +void main() +{ + int[int] f = {1: 1, 2: 2}; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/testInference.d b/gcc/testsuite/gdc.test/fail_compilation/testInference.d index 145fc9e8b9d6cb19353e3efa52cf861e7483a18b..b3a8a561cf9f0ef875a0918bd10f9e39b940ba55 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/testInference.d +++ b/gcc/testsuite/gdc.test/fail_compilation/testInference.d @@ -138,7 +138,7 @@ immutable(void)* g10063(inout int* p) pure TEST_OUTPUT: --- fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049` -fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda2` +fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda_L147_C14` fail_compilation/testInference.d(148): which calls `testInference.impure14049` fail_compilation/testInference.d(143): which wasn't inferred `pure` because of: fail_compilation/testInference.d(143): `pure` function `testInference.impure14049` cannot access mutable static data `i` diff --git a/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d b/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d index 8f6f145e6bf48dd8e4507c23f318a7d0b10b0c5f..66062fdab69c29cb8485ad9d858eaaa70c954d9f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d +++ b/gcc/testsuite/gdc.test/fail_compilation/traits_alone.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/traits_alone.d(11): Error: found `End of File` when expecting `(` fail_compilation/traits_alone.d(11): Error: `__traits(identifier, args...)` expected -fail_compilation/traits_alone.d(11): Error: no identifier for declarator `_error_` +fail_compilation/traits_alone.d(11): Error: no identifier for declarator `$r:_?_error_?$` --- */ //used to segfault diff --git a/gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d b/gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d index 8609d29b58cbc3a0ede1525ce30060e258747a07..0753b13f31857dd5817076e382de2a860e6d470b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d +++ b/gcc/testsuite/gdc.test/fail_compilation/var_func_attr.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda8` of type `void function() nothrow @nogc @safe` to `void function() pure` +fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda_L19_C27` of type `void function() nothrow @nogc @safe` to `void function() pure` --- */ diff --git a/gcc/testsuite/gdc.test/runnable/mangle.d b/gcc/testsuite/gdc.test/runnable/mangle.d index 53d7648099734d7dafdedac3d7ae7ad6923e3841..76a4adfab7fa888a956470a3ec8c8e6bd03a7f81 100644 --- a/gcc/testsuite/gdc.test/runnable/mangle.d +++ b/gcc/testsuite/gdc.test/runnable/mangle.d @@ -448,11 +448,11 @@ void test11776() if (1) { auto s = S11776!(a => 1)(); - static assert(typeof(s).mangleof == - "S"~"6mangle"~tl!("56")~ + enum expected = "S"~"6mangle"~tl!("56")~ ("__T"~"6S11776"~"S"~tl!("42")~ - (id!("6mangle","Qs")~"9test11776"~"FZ"~"9__lambda1MFZ"~id!("9__lambda1","Qn"))~"Z" - )~id!("6S11776", "QBm")); + (id!("6mangle","Qs")~"9test11776"~"FZ"~"17__lambda_L444_C14MFZ17__lambda_L450_C30")~"Z" + )~id!("6S11776", "QCm"); + static assert(typeof(s).mangleof == expected); } }; } @@ -509,7 +509,7 @@ void func12231a()() if (is(typeof({ class C {} static assert(C.mangleof == - "C6mangle"~tl!("16")~"__U10func12231aZ"~id!("10func12231a","Qn")~"FZ9__lambda1MFZ1C"); + "C6mangle"~tl!("16")~"__U10func12231aZ"~id!("10func12231a","Qn")~"FZ17__lambda_L509_C15MFZ1C"); // ### L # }))) {} @@ -517,13 +517,13 @@ if (is(typeof({ void func12231b()() if (is(typeof({ class C {} static assert(C.mangleof == - "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ9__lambda1MFZ1C"); + "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ17__lambda_L518_C15MFZ1C"); // L__L L LL })) && is(typeof({ class C {} static assert(C.mangleof == - "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ9__lambda2MFZ1C"); + "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ17__lambda_L523_C15MFZ1C"); // L__L L LL }))) {} @@ -532,14 +532,14 @@ void func12231c()() if (is(typeof({ class C {} static assert(C.mangleof == - "C6mangle"~tl!("16")~"__U10func12231cZ"~id!("10func12231c","Qn")~"FZ9__lambda1MFZ1C"); + "C6mangle"~tl!("16")~"__U10func12231cZ"~id!("10func12231c","Qn")~"FZ17__lambda_L532_C15MFZ1C"); // L__L L LL }))) { (){ class C {} static assert(C.mangleof == - "C6mangle"~tl!("16")~"__T10func12231cZ"~id!("10func12231c","Qn")~"FZ9__lambda1MFZ1C"); + "C6mangle"~tl!("16")~"__T10func12231cZ"~id!("10func12231c","Qn")~"FZ16__lambda_L539_C5MFZ1C"); // L__L L LL }(); } @@ -548,14 +548,14 @@ void func12231c(X)() if (is(typeof({ class C {} static assert(C.mangleof == - "C6mangle"~tl!("20")~"__U10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ9__lambda1MFZ1C"); + "C6mangle"~tl!("20")~"__U10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ17__lambda_L548_C15MFZ1C"); // L__L L___L LL }))) { (){ class C {} static assert(C.mangleof == - "C6mangle"~tl!("20")~"__T10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ9__lambda1MFZ1C"); + "C6mangle"~tl!("20")~"__T10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ16__lambda_L555_C5MFZ1C"); // L__L L___L LL }(); } @@ -616,7 +616,7 @@ static assert(funcd.mangleof == "_D6mangle5funcdFPFZNnZi"); struct S21753 { void function() f1; } void fun21753(S21753 v)() {} alias fl21753 = (){}; -static assert((fun21753!(S21753(fl21753))).mangleof == "_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10" ~ fl21753.stringof ~ "MFNaNbNiNfZvZQCbQp"); +static assert((fun21753!(S21753(fl21753))).mangleof == "_D6mangle__T8fun21753VSQv6S21753S1f_DQBj16" ~ fl21753.stringof ~ "MFNaNbNiNfZvZQChQp"); /***************************************************/ void main() diff --git a/gcc/testsuite/gdc.test/runnable/test24884.d b/gcc/testsuite/gdc.test/runnable/test24884.d new file mode 100644 index 0000000000000000000000000000000000000000..0070d2b932452a33be4fa84cb5e1a960587232c1 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test24884.d @@ -0,0 +1,34 @@ +/* +REQUIRED_ARGS: -inline +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24884 + +pragma(inline, false) +bool norm(int a) => 0; + +pragma(inline, false) +void inlinebug(ref double[4] point1, ref double[4] point2, ref double[4] point3, ref double[4] abcd) +{ + double[4] v1 = 0.0; + double[4] v2 = 0.0; + + v1[0] = point1[0] - point2[0]; + v1[1] = point1[1] - point2[1]; + v1[2] = point1[2] - point2[2]; + v1[3] = point1[3]; + v2[0] = point2[0] - point3[0]; + v2[1] = point2[1] - point3[1]; + v2[2] = point2[2] - point3[2]; + + int p = cast(int) &abcd; + int q = cast(int) &point1; + abcd[0] = norm(7) + p; + abcd[1] = q + p; +} + +extern(C) void main() +{ + double[4] a = 0.0; + inlinebug(a, a, a, a); +} diff --git a/gcc/testsuite/gdc.test/runnable/testkeyword.d b/gcc/testsuite/gdc.test/runnable/testkeyword.d index 5fb4d4420380503323d4c49a6652a306a752e3bb..7f8bded2543b9132ce826c7b1f58245cec99c6c8 100644 --- a/gcc/testsuite/gdc.test/runnable/testkeyword.d +++ b/gcc/testsuite/gdc.test/runnable/testkeyword.d @@ -97,8 +97,8 @@ void main(string[] args) nothrow auto funcLiteral = (int x, int y) { - enum thisFunc = "testkeyword.main.__lambda5"; - enum thisFunc2 = "testkeyword.main.__lambda5(int x, int y)"; + enum thisFunc = "testkeyword.main.__lambda_L98_C24"; + enum thisFunc2 = "testkeyword.main.__lambda_L98_C24(int x, int y)"; static assert(getFuncArgFile() == thisFile); static assert(getFuncArgLine() == 104); diff --git a/gcc/testsuite/gdc.test/runnable/traits.d b/gcc/testsuite/gdc.test/runnable/traits.d index 5186987deaea57354da92d5bae515532deeef318..7722d9dd6d9190c973ca8e0008df6c1175695639 100644 --- a/gcc/testsuite/gdc.test/runnable/traits.d +++ b/gcc/testsuite/gdc.test/runnable/traits.d @@ -10,7 +10,7 @@ Creating library {{RESULTS_DIR}}/runnable/traits_0.lib and object {{RESULTS_DIR} TRANSFORM_OUTPUT: remove_lines("Creating library") TEST_OUTPUT: --- -__lambda1 +__lambda_L1073_C5 --- */ diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index f660884c3b9590a9f318b1d3025a5c9b36456315..bfdc9ea21e1e45e373e84f60baddf81994375890 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -2b89c2909de239bd603d6f36379658fe902667db +82a5d2a7c4dd3d270537bcede2981e047bfd0e6a The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index a20bf6bdff76b398ca4fb5652360bbc9ca29e97a..7713c8cf5a8da322a1182ac009f059de0576e759 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -188,6 +188,7 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/internal/container/treap.d core/internal/convert.d \ core/internal/dassert.d core/internal/destruction.d \ core/internal/entrypoint.d core/internal/gc/bits.d \ + core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \ core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 53c197e5d89f0d4ef3b8192e95dc341249851dc1..f4d55523bfaa8784f557b55434eca65405ede1b9 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -209,6 +209,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ core/internal/container/treap.lo core/internal/convert.lo \ core/internal/dassert.lo core/internal/destruction.lo \ core/internal/entrypoint.lo core/internal/gc/bits.lo \ + core/internal/gc/blkcache.lo core/internal/gc/blockmeta.lo \ core/internal/gc/impl/conservative/gc.lo \ core/internal/gc/impl/manual/gc.lo \ core/internal/gc/impl/proto/gc.lo core/internal/gc/os.lo \ @@ -868,6 +869,7 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/internal/container/treap.d core/internal/convert.d \ core/internal/dassert.d core/internal/destruction.d \ core/internal/entrypoint.d core/internal/gc/bits.d \ + core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \ core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ @@ -1265,6 +1267,8 @@ core/internal/gc/$(am__dirstamp): @$(MKDIR_P) core/internal/gc @: > core/internal/gc/$(am__dirstamp) core/internal/gc/bits.lo: core/internal/gc/$(am__dirstamp) +core/internal/gc/blkcache.lo: core/internal/gc/$(am__dirstamp) +core/internal/gc/blockmeta.lo: core/internal/gc/$(am__dirstamp) core/internal/gc/impl/conservative/$(am__dirstamp): @$(MKDIR_P) core/internal/gc/impl/conservative @: > core/internal/gc/impl/conservative/$(am__dirstamp) diff --git a/libphobos/libdruntime/core/internal/array/arrayassign.d b/libphobos/libdruntime/core/internal/array/arrayassign.d index 6e3c1fdc3effd8389190f68ed13d13cfe2c31a2f..21690caf5d940e231c4fa5121d0726b02345b26f 100644 --- a/libphobos/libdruntime/core/internal/array/arrayassign.d +++ b/libphobos/libdruntime/core/internal/array/arrayassign.d @@ -347,7 +347,7 @@ Tarr _d_arraysetassign(Tarr : T[], T)(return scope Tarr to, scope ref T value) @ static if (__traits(isCopyable, T)) copyEmplace(value, dst); else - memcpy(cast(void*) &value, cast(void*) &dst, elemSize); + memcpy(cast(void*) &dst, cast(void*) &value, elemSize); auto elem = cast(Unqual!T*) &tmp; destroy(*elem); } @@ -395,6 +395,20 @@ Tarr _d_arraysetassign(Tarr : T[], T)(return scope Tarr to, scope ref T value) @ assert(arr == [S(1234), S(1234), S(1234), S(1234)]); } +// disabled copy constructor +@safe unittest +{ + static struct S + { + int val; + @disable this(ref S); + } + S[1] arr; + S s = S(1234); + _d_arraysetassign(arr[], s); + assert(arr[0].val == 1234); +} + // throwing and `nothrow` @safe nothrow unittest { diff --git a/libphobos/libdruntime/core/internal/array/utils.d b/libphobos/libdruntime/core/internal/array/utils.d index 89ce6ca21837868dda61d3bb9d0c065e7fb3929e..c45913d87e1b0d9483b3325da92c3733e879f506 100644 --- a/libphobos/libdruntime/core/internal/array/utils.d +++ b/libphobos/libdruntime/core/internal/array/utils.d @@ -263,111 +263,7 @@ void *__arrayStart()(return scope BlkInfo info) nothrow pure */ bool __setArrayAllocLength(T)(ref BlkInfo info, size_t newLength, bool isShared, size_t oldLength = ~0) { - import core.atomic; import core.lifetime : TypeInfoSize; - - size_t typeInfoSize = TypeInfoSize!T; - - if (info.size <= 256) - { - import core.checkedint; - - bool overflow; - auto newLengthPadded = addu(newLength, - addu(SMALLPAD, typeInfoSize, overflow), - overflow); - - if (newLengthPadded > info.size || overflow) - // new size does not fit inside block - return false; - - auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); - if (oldLength != ~0) - { - if (isShared) - { - return cas(cast(shared)length, cast(ubyte)oldLength, cast(ubyte)newLength); - } - else - { - if (*length == cast(ubyte)oldLength) - *length = cast(ubyte)newLength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ubyte)newLength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast()typeid(T); - } - } - else if (info.size < PAGESIZE) - { - if (newLength + MEDPAD + typeInfoSize > info.size) - // new size does not fit inside block - return false; - auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); - if (oldLength != ~0) - { - if (isShared) - { - return cas(cast(shared)length, cast(ushort)oldLength, cast(ushort)newLength); - } - else - { - if (*length == oldLength) - *length = cast(ushort)newLength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ushort)newLength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast()typeid(T); - } - } - else - { - if (newLength + LARGEPAD > info.size) - // new size does not fit inside block - return false; - auto length = cast(size_t *)(info.base); - if (oldLength != ~0) - { - if (isShared) - { - return cas(cast(shared)length, cast(size_t)oldLength, cast(size_t)newLength); - } - else - { - if (*length == oldLength) - *length = newLength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = newLength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); - *typeInfo = cast()typeid(T); - } - } - return true; // resize succeeded + import core.internal.gc.blockmeta : __setArrayAllocLengthImpl; + return __setArrayAllocLengthImpl(info, newLength, isShared, typeid(T), oldLength, TypeInfoSize!T); } diff --git a/libphobos/libdruntime/core/internal/gc/blkcache.d b/libphobos/libdruntime/core/internal/gc/blkcache.d new file mode 100644 index 0000000000000000000000000000000000000000..c555c22f636fb728e7e9d8d23ab7407dcd7abe04 --- /dev/null +++ b/libphobos/libdruntime/core/internal/gc/blkcache.d @@ -0,0 +1,243 @@ +/** +BlkInfo thread-local cache. Used for array appending in the conservative GC to avoid the lock when possible. + +Note: this used to be in rt.lifetime, but was moved here to allow GCs to take over array operations. +*/ +module core.internal.gc.blkcache; + +import core.memory; +import core.attribute; + +alias BlkInfo = GC.BlkInfo; +alias BlkAttr = GC.BlkAttr; + +/** + cache for the lookup of the block info + */ +private enum N_CACHE_BLOCKS = 8; + +// note this is TLS, so no need to sync. +BlkInfo *__blkcache_storage; + +static if (N_CACHE_BLOCKS == 1) +{ + version=single_cache; +} +else +{ + //version=simple_cache; // uncomment to test simple cache strategy + //version=random_cache; // uncomment to test random cache strategy + + // ensure N_CACHE_BLOCKS is power of 2. + static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS)); + + version (random_cache) + { + int __nextRndNum = 0; + } + int __nextBlkIdx; +} + +@property BlkInfo *__blkcache() nothrow +{ + if (!__blkcache_storage) + { + import core.stdc.stdlib; + import core.stdc.string; + import core.thread.threadbase; + auto tBase = ThreadBase.getThis(); + if (tBase is null) + // if we don't have a thread object, this is a detached thread, and + // this won't be properly maintained by the GC. + return null; + + // allocate the block cache for the first time + immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS; + // use C alloc, because this may become a detached thread, and the GC + // would then clean up the cache without zeroing this pointer. + __blkcache_storage = cast(BlkInfo*) calloc(size, 1); + tBase.tlsGCData = __blkcache_storage; + } + return __blkcache_storage; +} + +// free the allocation on thread exit. +@standalone static ~this() +{ + if (__blkcache_storage) + { + import core.stdc.stdlib; + import core.thread.threadbase; + auto tBase = ThreadBase.getThis(); + if (tBase !is null) + tBase.tlsGCData = null; + free(__blkcache_storage); + __blkcache_storage = null; + } +} + +/** + * Indicates whether an address has been marked by the GC. + */ +enum IsMarked : int +{ + no, /// Address is not marked. + yes, /// Address is marked. + unknown, /// Address is not managed by the GC. +} + +alias IsMarkedDg = IsMarked delegate(void* addr) nothrow; /// The isMarked callback function. + +// we expect this to be called with the lock in place +void processGCMarks(void* data, scope IsMarkedDg isMarked) nothrow +{ + if (!data) + return; + + auto cache = cast(BlkInfo*) data; + // called after the mark routine to eliminate block cache data when it + // might be ready to sweep + + debug(PRINTF) printf("processing GC Marks, %x\n", cache); + debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS) + { + printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr); + } + auto cache_end = cache + N_CACHE_BLOCKS; + for (;cache < cache_end; ++cache) + { + if (cache.base != null && isMarked(cache.base) == IsMarked.no) + { + debug(PRINTF) printf("clearing cache entry at %x\n", cache.base); + cache.base = null; // clear that data. + } + } +} + +unittest +{ + // Bugzilla 10701 - segfault in GC + ubyte[] result; result.length = 4096; + GC.free(result.ptr); + GC.collect(); +} + +/** + Get the cached block info of an interior pointer. Returns null if the + interior pointer's block is not cached. + + NOTE: The following note was not valid, but is retained for historical + purposes. The data cannot be cleared because the stack contains a + reference to the affected block (e.g. through `interior`). Therefore, + the element will not be collected, and the data will remain valid. + + ORIGINAL: The base ptr in this struct can be cleared asynchronously by the GC, + so any use of the returned BlkInfo should copy it and then check the + base ptr of the copy before actually using it. + */ +BlkInfo *__getBlkInfo(void *interior) nothrow +{ + BlkInfo *ptr = __blkcache; + if (ptr is null) + // if for some reason we don't have a cache, return null. + return null; + version (single_cache) + { + if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) + return ptr; + return null; // not in cache. + } + else version (simple_cache) + { + foreach (i; 0..N_CACHE_BLOCKS) + { + if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) + return ptr; + ptr++; + } + } + else + { + // try to do a smart lookup, using __nextBlkIdx as the "head" + auto curi = ptr + __nextBlkIdx; + for (auto i = curi; i >= ptr; --i) + { + if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) + return i; + } + + for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i) + { + if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) + return i; + } + } + return null; // not in cache. +} + +void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow +{ + auto cache = __blkcache; + if (cache is null) + // no cache to use. + return; + + version (single_cache) + { + *cache = bi; + return; + } + else + { + version (simple_cache) + { + if (curpos) + *curpos = bi; + else + { + // note, this is a super-simple algorithm that does not care about + // most recently used. It simply uses a round-robin technique to + // cache block info. This means that the ordering of the cache + // doesn't mean anything. Certain patterns of allocation may + // render the cache near-useless. + cache[__nextBlkIdx] = bi; + __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); + } + } + else version (random_cache) + { + // strategy: if the block currently is in the cache, move the + // current block index to the a random element and evict that + // element. + if (!curpos) + { + __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1); + curpos = cache + __nextBlkIdx; + } + else + { + __nextBlkIdx = curpos - cache; + } + *curpos = bi; + } + else + { + // + // strategy: If the block currently is in the cache, swap it with + // the head element. Otherwise, move the head element up by one, + // and insert it there. + // + if (!curpos) + { + __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); + curpos = cache + __nextBlkIdx; + } + else if (curpos !is cache + __nextBlkIdx) + { + *curpos = cache[__nextBlkIdx]; + curpos = cache + __nextBlkIdx; + } + *curpos = bi; + } + } +} diff --git a/libphobos/libdruntime/core/internal/gc/blockmeta.d b/libphobos/libdruntime/core/internal/gc/blockmeta.d new file mode 100644 index 0000000000000000000000000000000000000000..c7dfeb6507529e9cf560ea0ec155e66b0760fa5e --- /dev/null +++ b/libphobos/libdruntime/core/internal/gc/blockmeta.d @@ -0,0 +1,209 @@ +/** + Functions to manipulate metadata in-block. + + functionality was moved from rt.lifetime + */ +module core.internal.gc.blockmeta; + +import core.memory; + +alias BlkInfo = GC.BlkInfo; +alias BlkAttr = GC.BlkAttr; + +enum : size_t +{ + PAGESIZE = 4096, + BIGLENGTHMASK = ~(PAGESIZE - 1), + SMALLPAD = 1, + MEDPAD = ushort.sizeof, + LARGEPREFIX = 16, // 16 bytes padding at the front of the array + LARGEPAD = LARGEPREFIX + 1, + MAXSMALLSIZE = 256-SMALLPAD, + MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD +} + +// size used to store the TypeInfo at the end of an allocation for structs that have a destructor +size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc +{ + if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + { + auto sti = cast(TypeInfo_Struct)cast(void*)ti; + if (sti.xdtor) + return size_t.sizeof; + } + return 0; +} + +/** + Set the allocated length of the array block. This is called + any time an array is appended to or its length is set. + + The allocated block looks like this for blocks < PAGESIZE: + + |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize| + + + The size of the allocated length at the end depends on the block size: + + a block of 16 to 256 bytes has an 8-bit length. + + a block with 512 to pagesize/2 bytes has a 16-bit length. + + For blocks >= pagesize, the length is a size_t and is at the beginning of the + block. The reason we have to do this is because the block can extend into + more pages, so we cannot trust the block length if it sits at the end of the + block, because it might have just been extended. If we can prove in the + future that the block is unshared, we may be able to change this, but I'm not + sure it's important. + + In order to do put the length at the front, we have to provide 16 bytes + buffer space in case the block has to be aligned properly. In x86, certain + SSE instructions will only work if the data is 16-byte aligned. In addition, + we need the sentinel byte to prevent accidental pointers to the next block. + Because of the extra overhead, we only do this for page size and above, where + the overhead is minimal compared to the block size. + + So for those blocks, it looks like: + + |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte| + + where elem0 starts 16 bytes after the first byte. + */ +bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow +{ + size_t typeInfoSize = structTypeInfoSize(tinext); + return __setArrayAllocLengthImpl(info, newlength, isshared, tinext, oldlength, typeInfoSize); +} + +// the impl function, used both above and in core.internal.array.utils +bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength, size_t typeInfoSize) pure nothrow +{ + import core.atomic; + + if (info.size <= 256) + { + import core.checkedint; + + bool overflow; + auto newlength_padded = addu(newlength, + addu(SMALLPAD, typeInfoSize, overflow), + overflow); + + if (newlength_padded > info.size || overflow) + // new size does not fit inside block + return false; + + auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); + if (oldlength != ~0) + { + if (isshared) + { + return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength); + } + else + { + if (*length == cast(ubyte)oldlength) + *length = cast(ubyte)newlength; + else + return false; + } + } + else + { + // setting the initial length, no cas needed + *length = cast(ubyte)newlength; + } + if (typeInfoSize) + { + auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); + *typeInfo = cast() tinext; + } + } + else if (info.size < PAGESIZE) + { + if (newlength + MEDPAD + typeInfoSize > info.size) + // new size does not fit inside block + return false; + auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); + if (oldlength != ~0) + { + if (isshared) + { + return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength); + } + else + { + if (*length == oldlength) + *length = cast(ushort)newlength; + else + return false; + } + } + else + { + // setting the initial length, no cas needed + *length = cast(ushort)newlength; + } + if (typeInfoSize) + { + auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); + *typeInfo = cast() tinext; + } + } + else + { + if (newlength + LARGEPAD > info.size) + // new size does not fit inside block + return false; + auto length = cast(size_t *)(info.base); + if (oldlength != ~0) + { + if (isshared) + { + return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength); + } + else + { + if (*length == oldlength) + *length = newlength; + else + return false; + } + } + else + { + // setting the initial length, no cas needed + *length = newlength; + } + if (typeInfoSize) + { + auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); + *typeInfo = cast()tinext; + } + } + return true; // resize succeeded +} + +/** + get the allocation size of the array for the given block (without padding or type info) + */ +size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow +{ + if (info.size <= 256) + return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); + + if (info.size < PAGESIZE) + return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD); + + return *cast(size_t *)(info.base); +} + +/** + get the padding required to allocate size bytes. Note that the padding is + NOT included in the passed in size. Therefore, do NOT call this function + with the size of an allocated block. + */ +size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted +{ + return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); +} diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index dd6f92a8a7ab5fc41d59dab8cc507dae28adbdd7..149cc5d4cfbd2459d4d56f2903f9ec330f3dc90e 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -41,6 +41,7 @@ import core.gc.gcinterface; import core.internal.container.treap; import core.internal.spinlock; import core.internal.gc.pooltable; +import core.internal.gc.blkcache; import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; import core.stdc.string : memcpy, memset, memmove; @@ -1426,7 +1427,7 @@ short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase() foreach (i, size; binsize) { - short end = (PAGESIZE / size) * size; + short end = cast(short) ((PAGESIZE / size) * size); short bsz = size / 16; foreach (off; 0..PAGESIZE/16) { @@ -2873,7 +2874,7 @@ struct Gcx markProcPid = 0; // process GC marks then sweep thread_suspendAll(); - thread_processGCMarks(&isMarked); + thread_processTLSGCData(&clearBlkCacheData); thread_resumeAll(); break; case ChildStatus.running: @@ -3108,7 +3109,7 @@ Lmark: markAll!(markConservative!false)(); } - thread_processGCMarks(&isMarked); + thread_processTLSGCData(&clearBlkCacheData); thread_resumeAll(); isFinal = false; } @@ -3161,13 +3162,27 @@ Lmark: return freedPages; } + /** + * Clear the block cache data if it exists, given the data which is the + * block info cache. + * + * Warning! This should only be called while the world is stopped inside + * the fullcollect function after all live objects have been marked, but + * before sweeping. + */ + void *clearBlkCacheData(void* data) scope nothrow + { + processGCMarks(data, &isMarked); + return data; + } + /** * Returns true if the addr lies within a marked block. * * Warning! This should only be called while the world is stopped inside * the fullcollect function after all live objects have been marked, but before sweeping. */ - int isMarked(void *addr) scope nothrow + IsMarked isMarked(void *addr) scope nothrow { // first, we find the Pool this block is in, then check to see if the // mark bit is clear. diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d index f0d9ebc9a81362b41005ea76f7607373c377698e..02898082e402a6010f9e8e8e05a4e40e6754a4d2 100644 --- a/libphobos/libdruntime/core/internal/traits.d +++ b/libphobos/libdruntime/core/internal/traits.d @@ -267,8 +267,12 @@ template hasElaborateDestructor(S) } else static if (is(S == struct)) { - enum hasElaborateDestructor = __traits(hasMember, S, "__dtor") - || anySatisfy!(.hasElaborateDestructor, Fields!S); + // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then + // this should be the implementation, but until that's fixed, we need the + // uncommented code. + // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor"); + + enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]); } else { @@ -276,6 +280,64 @@ template hasElaborateDestructor(S) } } +private bool hasDtor(string[] members) +{ + foreach (name; members) + { + if (name == "__xdtor") + return true; + } + + return false; +} + +@safe unittest +{ + static struct NoDestructor {} + static assert(!hasElaborateDestructor!NoDestructor); + static assert(!hasElaborateDestructor!(NoDestructor[42])); + static assert(!hasElaborateDestructor!(NoDestructor[0])); + static assert(!hasElaborateDestructor!(NoDestructor[])); + + static struct HasDestructor { ~this() {} } + static assert( hasElaborateDestructor!HasDestructor); + static assert( hasElaborateDestructor!(HasDestructor[42])); + static assert(!hasElaborateDestructor!(HasDestructor[0])); + static assert(!hasElaborateDestructor!(HasDestructor[])); + + static struct HasDestructor2 { HasDestructor s; } + static assert( hasElaborateDestructor!HasDestructor2); + static assert( hasElaborateDestructor!(HasDestructor2[42])); + static assert(!hasElaborateDestructor!(HasDestructor2[0])); + static assert(!hasElaborateDestructor!(HasDestructor2[])); + + static class HasFinalizer { ~this() {} } + static assert(!hasElaborateDestructor!HasFinalizer); + + static struct HasUnion { union { HasDestructor s; } } + static assert(!hasElaborateDestructor!HasUnion); + static assert(!hasElaborateDestructor!(HasUnion[42])); + static assert(!hasElaborateDestructor!(HasUnion[0])); + static assert(!hasElaborateDestructor!(HasUnion[])); + + static assert(!hasElaborateDestructor!int); + static assert(!hasElaborateDestructor!(int[0])); + static assert(!hasElaborateDestructor!(int[42])); + static assert(!hasElaborateDestructor!(int[])); +} + +// https://issues.dlang.org/show_bug.cgi?id=24865 +@safe unittest +{ + static struct S2 { ~this() {} } + static struct S3 { S2 field; } + static struct S6 { S3[0] field; } + + static assert( hasElaborateDestructor!S2); + static assert( hasElaborateDestructor!S3); + static assert(!hasElaborateDestructor!S6); +} + // std.traits.hasElaborateCopyDestructor template hasElaborateCopyConstructor(S) { @@ -302,7 +364,7 @@ template hasElaborateCopyConstructor(S) this(int x, int y) {} } - static assert(hasElaborateCopyConstructor!S); + static assert( hasElaborateCopyConstructor!S); static assert(!hasElaborateCopyConstructor!(S[0][1])); static struct S2 @@ -320,7 +382,11 @@ template hasElaborateCopyConstructor(S) this(int x, int y) {} } - static assert(hasElaborateCopyConstructor!S3); + static assert( hasElaborateCopyConstructor!S3); + + static struct S4 { union { S s; } } + + static assert(!hasElaborateCopyConstructor!S4); } template hasElaborateAssign(S) @@ -332,8 +398,7 @@ template hasElaborateAssign(S) else static if (is(S == struct)) { enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || - is(typeof(S.init.opAssign(lvalueOf!S))) || - anySatisfy!(.hasElaborateAssign, Fields!S); + is(typeof(S.init.opAssign(lvalueOf!S))); } else { @@ -341,17 +406,148 @@ template hasElaborateAssign(S) } } +unittest +{ + { + static struct S {} + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { int i; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(ref S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(int) {} } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { this(this) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + // https://issues.dlang.org/show_bug.cgi?id=24834 + /+ + { + static struct S { this(ref S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + +/ + { + static struct S { ~this() {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { @disable void opAssign(S); } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member {} + static struct S { Member member; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { Member member; } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member {} + static struct S { Member member; void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { @disable void opAssign(Member); } + static struct S { Member member; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { @disable void opAssign(Member); } + static struct S { Member member; void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { Member member; @disable void opAssign(S); } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { union { Member member; } } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + + static assert(!hasElaborateAssign!int); + static assert(!hasElaborateAssign!(string[])); + static assert(!hasElaborateAssign!Object); +} + template hasIndirections(T) { static if (is(T == struct) || is(T == union)) enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof)); + else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface)) + enum hasIndirections = true; else static if (is(T == E[N], E, size_t N)) enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E); else static if (isFunctionPointer!T) enum hasIndirections = false; else - enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T || - __traits(isAssociativeArray, T) || is (T == class) || is(T == interface); + enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T; } template hasUnsharedIndirections(T) diff --git a/libphobos/libdruntime/core/lifetime.d b/libphobos/libdruntime/core/lifetime.d index 7010d2ad3b08361fbfce5fa8520d6463f58fa4d4..84ffdde7a2cbdb45802778cf324aa31922962665 100644 --- a/libphobos/libdruntime/core/lifetime.d +++ b/libphobos/libdruntime/core/lifetime.d @@ -2739,8 +2739,11 @@ if (is(T == class)) auto init = __traits(initSymbol, T); void* p; - static if (__traits(getLinkage, T) == "Windows") + static if (__traits(isCOMClass, T)) { + // If this is a COM class we allocate it using malloc. + // This allows the reference counting to outlive the reference known about by the GC. + p = pureMalloc(init.length); if (!p) onOutOfMemoryError(); diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d index 001c31596784187fefc119f659e68c5a1898173c..63a3c2e04c8035bb30f4df23adf94001cf599173 100644 --- a/libphobos/libdruntime/core/memory.d +++ b/libphobos/libdruntime/core/memory.d @@ -100,6 +100,10 @@ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly, Alex Rønne Petersen * Source: $(DRUNTIMESRC core/_memory.d) + * Macros: + * WARN_UNINITIALIZED=$(RED Warning): + * $1 will be uninitialized, and may happen to hold pointers to GC memory. + * Consider zeroing out any uninitialized bytes which won't be immediately written to. */ module core.memory; @@ -400,7 +404,7 @@ extern(D): * a = A bit field containing any bits to set for this memory block. * * Returns: - * The result of a call to getAttr after the specified bits have been + * The result of a call to $(LREF getAttr) after the specified bits have been * set. */ static uint setAttr( const scope void* p, uint a ) nothrow @@ -427,7 +431,7 @@ extern(D): * a = A bit field containing any bits to clear for this memory block. * * Returns: - * The result of a call to getAttr after the specified bits have been + * The result of a call to $(LREF getAttr) after the specified bits have been * cleared. */ static uint clrAttr( const scope void* p, uint a ) nothrow @@ -461,6 +465,8 @@ extern(C): * A reference to the allocated memory or null if insufficient memory * is available. * + * $(WARN_UNINITIALIZED Allocated memory) + * * Throws: * OutOfMemoryError on allocation failure. */ @@ -472,7 +478,7 @@ extern(C): /** * Requests an aligned block of managed memory from the garbage collector. - * This memory may be deleted at will with a call to free, or it may be + * This memory may be deleted at will with a call to $(LREF free), or it may be * discarded and cleaned up automatically during a collection run. If * allocation fails, this function will call onOutOfMemory which is * expected to throw an OutOfMemoryError. @@ -487,6 +493,8 @@ extern(C): * Information regarding the allocated memory block or BlkInfo.init on * error. * + * $(WARN_UNINITIALIZED Allocated memory) + * * Throws: * OutOfMemoryError on allocation failure. */ @@ -564,6 +572,8 @@ extern(C): * zero or the pointer does not point to the base of an GC allocated * memory block. * + * $(WARN_UNINITIALIZED Any extra bytes past the initial size) + * * Throws: * `OutOfMemoryError` on allocation failure. */ @@ -608,6 +618,8 @@ extern(C): * The size in bytes of the extended memory block referenced by p or zero * if no extension occurred. * + * $(WARN_UNINITIALIZED Any extension bytes) + * * Note: * Extend may also be used to extend slices (or memory blocks with * $(LREF APPENDABLE) info). However, use the return value only @@ -669,7 +681,7 @@ extern(C): * If p references memory not originally allocated by this garbage * collector, if p points to the interior of a memory block, or if this * method is called from a finalizer, no action will be taken. The block - * will not be finalized regardless of whether the FINALIZE attribute is + * will not be finalized regardless of whether the $(LREF FINALIZE) attribute is * set. If finalization is desired, call $(REF1 destroy, object) prior to `GC.free`. * * Params: @@ -707,7 +719,7 @@ extern(D): /** * Returns the true size of the memory block referenced by p. This value - * represents the maximum number of bytes for which a call to realloc may + * represents the maximum number of bytes for which a call to $(LREF realloc) may * resize the existing block in place. If p references memory not * originally allocated by this garbage collector, points to the interior * of a memory block, or if p is null, zero will be returned. diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d index cf93094937c0928e18d6951f1907decbeec2d293..2379f7925e4175c7e065bab16b116e3c15545845 100644 --- a/libphobos/libdruntime/core/thread/osthread.d +++ b/libphobos/libdruntime/core/thread/osthread.d @@ -1237,7 +1237,7 @@ private extern (D) ThreadBase attachThread(ThreadBase _thisThread) @nogc nothrow atomicStore!(MemoryOrder.raw)(thisThread.toThread.m_isRunning, true); } thisThread.m_isDaemon = true; - thisThread.tlsGCdataInit(); + thisThread.tlsRTdataInit(); Thread.setThis( thisThread ); version (Darwin) @@ -1312,7 +1312,7 @@ version (Windows) if ( addr == GetCurrentThreadId() ) { thisThread.m_hndl = GetCurrentThreadHandle(); - thisThread.tlsGCdataInit(); + thisThread.tlsRTdataInit(); Thread.setThis( thisThread ); } else @@ -1320,7 +1320,7 @@ version (Windows) thisThread.m_hndl = OpenThreadHandle( addr ); impersonate_thread(addr, { - thisThread.tlsGCdataInit(); + thisThread.tlsRTdataInit(); Thread.setThis( thisThread ); }); } diff --git a/libphobos/libdruntime/core/thread/threadbase.d b/libphobos/libdruntime/core/thread/threadbase.d index cb13e9ac0a0a585b534e4e8f50fe141f295b3218..f3854076b0bfb3b2dafcc0cc0df95c6c2b39c765 100644 --- a/libphobos/libdruntime/core/thread/threadbase.d +++ b/libphobos/libdruntime/core/thread/threadbase.d @@ -32,9 +32,6 @@ private alias ScanDg = void delegate(void* pstart, void* pend) nothrow; alias rt_tlsgc_scan = externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow); - - alias rt_tlsgc_processGCMarks = - externDFunc!("rt.tlsgc.processGCMarks", void function(void*, scope IsMarkedDg) nothrow); } @@ -131,9 +128,14 @@ class ThreadBase return (no_context || not_registered); } - package void tlsGCdataInit() nothrow @nogc + ref void* tlsGCData() nothrow @nogc { - m_tlsgcdata = rt_tlsgc_init(); + return m_tlsgcdata; + } + + package void tlsRTdataInit() nothrow @nogc + { + m_tlsrtdata = rt_tlsgc_init(); } package void initDataStorage() nothrow @@ -142,18 +144,18 @@ class ThreadBase m_main.bstack = getStackBottom(); m_main.tstack = m_main.bstack; - tlsGCdataInit(); + tlsRTdataInit(); } package void destroyDataStorage() nothrow @nogc { - rt_tlsgc_destroy(m_tlsgcdata); - m_tlsgcdata = null; + rt_tlsgc_destroy(m_tlsrtdata); + m_tlsrtdata = null; } package void destroyDataStorageIfAvail() nothrow @nogc { - if (m_tlsgcdata) + if (m_tlsrtdata) destroyDataStorage(); } @@ -477,6 +479,7 @@ package(core.thread): StackContext* m_curr; bool m_lock; private void* m_tlsgcdata; + private void* m_tlsrtdata; /////////////////////////////////////////////////////////////////////////// // Thread Context and GC Scanning Support @@ -1112,8 +1115,8 @@ private void scanAllTypeImpl(scope ScanAllThreadsTypeFn scan, void* curStackTop) scanWindowsOnly(scan, t); } - if (t.m_tlsgcdata !is null) - rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2)); + if (t.m_tlsrtdata !is null) + rt_tlsgc_scan(t.m_tlsrtdata, (p1, p2) => scan(ScanType.tls, p1, p2)); } } @@ -1163,43 +1166,15 @@ package void onThreadError(string msg) nothrow @nogc } -/** - * Indicates whether an address has been marked by the GC. - */ -enum IsMarked : int -{ - no, /// Address is not marked. - yes, /// Address is marked. - unknown, /// Address is not managed by the GC. -} - -alias IsMarkedDg = int delegate(void* addr) nothrow; /// The isMarked callback function. +// GC-specific processing of TLSGC data. +alias ProcessTLSGCDataDg = void* delegate(void* data) nothrow; -/** - * This routine allows the runtime to process any special per-thread handling - * for the GC. This is needed for taking into account any memory that is - * referenced by non-scanned pointers but is about to be freed. That currently - * means the array append cache. - * - * Params: - * isMarked = The function used to check if $(D addr) is marked. - * - * In: - * This routine must be called just prior to resuming all threads. - */ -extern(C) void thread_processGCMarks(scope IsMarkedDg isMarked) nothrow +void thread_processTLSGCData(ProcessTLSGCDataDg dg) nothrow { for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next) - { - /* Can be null if collection was triggered between adding a - * thread and calling rt_tlsgc_init. - */ - if (t.m_tlsgcdata !is null) - rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked); - } + t.m_tlsgcdata = dg(t.m_tlsgcdata); } - /** * Returns the stack top of the currently active stack within the calling * thread. diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 1de993c0bff97501cb5efd86d669889ce5eae641..86f5f820cc024ee43d880f2e088cbcf5cd2f2e3d 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -15,6 +15,8 @@ module rt.lifetime; import core.attribute : weak; import core.internal.array.utils : __arrayStart, __arrayClearPad; import core.memory; +import core.internal.gc.blkcache; +import core.internal.gc.blockmeta; debug(PRINTF) import core.stdc.stdio; static import rt.tlsgc; @@ -28,17 +30,6 @@ private extern (C) void _d_monitordelete(Object h, bool det); - enum : size_t - { - PAGESIZE = 4096, - BIGLENGTHMASK = ~(PAGESIZE - 1), - SMALLPAD = 1, - MEDPAD = ushort.sizeof, - LARGEPREFIX = 16, // 16 bytes padding at the front of the array - LARGEPAD = LARGEPREFIX + 1, - MAXSMALLSIZE = 256-SMALLPAD, - MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD - } } // Now-removed symbol, kept around for ABI @@ -211,191 +202,6 @@ inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc return ti; } -// size used to store the TypeInfo at the end of an allocation for structs that have a destructor -size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc -{ - if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast - { - auto sti = cast(TypeInfo_Struct)cast(void*)ti; - if (sti.xdtor) - return size_t.sizeof; - } - return 0; -} - -/** dummy class used to lock for shared array appending */ -private class ArrayAllocLengthLock -{} - -/** - Set the allocated length of the array block. This is called - any time an array is appended to or its length is set. - - The allocated block looks like this for blocks < PAGESIZE: - - |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize| - - - The size of the allocated length at the end depends on the block size: - - a block of 16 to 256 bytes has an 8-bit length. - - a block with 512 to pagesize/2 bytes has a 16-bit length. - - For blocks >= pagesize, the length is a size_t and is at the beginning of the - block. The reason we have to do this is because the block can extend into - more pages, so we cannot trust the block length if it sits at the end of the - block, because it might have just been extended. If we can prove in the - future that the block is unshared, we may be able to change this, but I'm not - sure it's important. - - In order to do put the length at the front, we have to provide 16 bytes - buffer space in case the block has to be aligned properly. In x86, certain - SSE instructions will only work if the data is 16-byte aligned. In addition, - we need the sentinel byte to prevent accidental pointers to the next block. - Because of the extra overhead, we only do this for page size and above, where - the overhead is minimal compared to the block size. - - So for those blocks, it looks like: - - |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte| - - where elem0 starts 16 bytes after the first byte. - */ -bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow -{ - import core.atomic; - - size_t typeInfoSize = structTypeInfoSize(tinext); - - if (info.size <= 256) - { - import core.checkedint; - - bool overflow; - auto newlength_padded = addu(newlength, - addu(SMALLPAD, typeInfoSize, overflow), - overflow); - - if (newlength_padded > info.size || overflow) - // new size does not fit inside block - return false; - - auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); - if (oldlength != ~0) - { - if (isshared) - { - return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength); - } - else - { - if (*length == cast(ubyte)oldlength) - *length = cast(ubyte)newlength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ubyte)newlength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast() tinext; - } - } - else if (info.size < PAGESIZE) - { - if (newlength + MEDPAD + typeInfoSize > info.size) - // new size does not fit inside block - return false; - auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); - if (oldlength != ~0) - { - if (isshared) - { - return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength); - } - else - { - if (*length == oldlength) - *length = cast(ushort)newlength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ushort)newlength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast() tinext; - } - } - else - { - if (newlength + LARGEPAD > info.size) - // new size does not fit inside block - return false; - auto length = cast(size_t *)(info.base); - if (oldlength != ~0) - { - if (isshared) - { - return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength); - } - else - { - if (*length == oldlength) - *length = newlength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = newlength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); - *typeInfo = cast()tinext; - } - } - return true; // resize succeeded -} - -/** - get the allocation size of the array for the given block (without padding or type info) - */ -private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow -{ - if (info.size <= 256) - return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); - - if (info.size < PAGESIZE) - return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD); - - return *cast(size_t *)(info.base); -} - -/** - get the padding required to allocate size bytes. Note that the padding is - NOT included in the passed in size. Therefore, do NOT call this function - with the size of an allocated block. - */ -private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted -{ - return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); -} - /** allocate an array memory block by applying the proper padding and assigning block attributes if not inherited from the existing block @@ -442,206 +248,6 @@ private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeI return bi; } -/** - cache for the lookup of the block info - */ -private enum N_CACHE_BLOCKS=8; - -// note this is TLS, so no need to sync. -BlkInfo *__blkcache_storage; - -static if (N_CACHE_BLOCKS==1) -{ - version=single_cache; -} -else -{ - //version=simple_cache; // uncomment to test simple cache strategy - //version=random_cache; // uncomment to test random cache strategy - - // ensure N_CACHE_BLOCKS is power of 2. - static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS)); - - version (random_cache) - { - int __nextRndNum = 0; - } - int __nextBlkIdx; -} - -@property BlkInfo *__blkcache() nothrow -{ - if (!__blkcache_storage) - { - import core.stdc.stdlib; - import core.stdc.string; - // allocate the block cache for the first time - immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS; - __blkcache_storage = cast(BlkInfo *)malloc(size); - memset(__blkcache_storage, 0, size); - } - return __blkcache_storage; -} - -// called when thread is exiting. -static ~this() -{ - // free the blkcache - if (__blkcache_storage) - { - import core.stdc.stdlib; - free(__blkcache_storage); - __blkcache_storage = null; - } -} - - -// we expect this to be called with the lock in place -void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) nothrow -{ - // called after the mark routine to eliminate block cache data when it - // might be ready to sweep - - debug(PRINTF) printf("processing GC Marks, %x\n", cache); - if (cache) - { - debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS) - { - printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr); - } - auto cache_end = cache + N_CACHE_BLOCKS; - for (;cache < cache_end; ++cache) - { - if (cache.base != null && !isMarked(cache.base)) - { - debug(PRINTF) printf("clearing cache entry at %x\n", cache.base); - cache.base = null; // clear that data. - } - } - } -} - -unittest -{ - // Bugzilla 10701 - segfault in GC - ubyte[] result; result.length = 4096; - GC.free(result.ptr); - GC.collect(); -} - -/** - Get the cached block info of an interior pointer. Returns null if the - interior pointer's block is not cached. - - NOTE: The base ptr in this struct can be cleared asynchronously by the GC, - so any use of the returned BlkInfo should copy it and then check the - base ptr of the copy before actually using it. - - TODO: Change this function so the caller doesn't have to be aware of this - issue. Either return by value and expect the caller to always check - the base ptr as an indication of whether the struct is valid, or set - the BlkInfo as a side-effect and return a bool to indicate success. - */ -BlkInfo *__getBlkInfo(void *interior) nothrow -{ - BlkInfo *ptr = __blkcache; - version (single_cache) - { - if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) - return ptr; - return null; // not in cache. - } - else version (simple_cache) - { - foreach (i; 0..N_CACHE_BLOCKS) - { - if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) - return ptr; - ptr++; - } - } - else - { - // try to do a smart lookup, using __nextBlkIdx as the "head" - auto curi = ptr + __nextBlkIdx; - for (auto i = curi; i >= ptr; --i) - { - if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) - return i; - } - - for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i) - { - if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) - return i; - } - } - return null; // not in cache. -} - -void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow -{ - version (single_cache) - { - *__blkcache = bi; - } - else - { - version (simple_cache) - { - if (curpos) - *curpos = bi; - else - { - // note, this is a super-simple algorithm that does not care about - // most recently used. It simply uses a round-robin technique to - // cache block info. This means that the ordering of the cache - // doesn't mean anything. Certain patterns of allocation may - // render the cache near-useless. - __blkcache[__nextBlkIdx] = bi; - __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); - } - } - else version (random_cache) - { - // strategy: if the block currently is in the cache, move the - // current block index to the a random element and evict that - // element. - auto cache = __blkcache; - if (!curpos) - { - __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1); - curpos = cache + __nextBlkIdx; - } - else - { - __nextBlkIdx = curpos - cache; - } - *curpos = bi; - } - else - { - // - // strategy: If the block currently is in the cache, swap it with - // the head element. Otherwise, move the head element up by one, - // and insert it there. - // - auto cache = __blkcache; - if (!curpos) - { - __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); - curpos = cache + __nextBlkIdx; - } - else if (curpos !is cache + __nextBlkIdx) - { - *curpos = cache[__nextBlkIdx]; - curpos = cache + __nextBlkIdx; - } - *curpos = bi; - } - } -} - /** Shrink the "allocated" length of an array to be the exact size of the array. diff --git a/libphobos/libdruntime/rt/tlsgc.d b/libphobos/libdruntime/rt/tlsgc.d index b13a1b319cf0678181333a24b9e329b033acc68c..f1dcc598d7964971fa8d9552659debf6a5307aae 100644 --- a/libphobos/libdruntime/rt/tlsgc.d +++ b/libphobos/libdruntime/rt/tlsgc.d @@ -23,7 +23,6 @@ static import rt.lifetime, rt.sections; struct Data { typeof(rt.sections.initTLSRanges()) tlsRanges; - rt.lifetime.BlkInfo** blockInfoCache; } /** @@ -39,8 +38,6 @@ void* init() nothrow @nogc // do module specific initialization data.tlsRanges = rt.sections.initTLSRanges(); - data.blockInfoCache = &rt.lifetime.__blkcache_storage; - return data; } @@ -67,16 +64,3 @@ void scan(void* data, scope ScanDg dg) nothrow // do module specific marking rt.sections.scanTLSRanges((cast(Data*)data).tlsRanges, dg); } - -alias int delegate(void* addr) nothrow IsMarkedDg; - -/** - * GC sweep hook, called FOR each thread. Can be used to free - * additional thread local memory or associated data structures. Note - * that only memory allocated from the GC can have marks. - */ -void processGCMarks(void* data, scope IsMarkedDg dg) nothrow -{ - // do module specific sweeping - rt.lifetime.processGCMarks(*(cast(Data*)data).blockInfoCache, dg); -} diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index f7c522322632c6a1473017aafe90b52be287e178..0522cf85a3a8ba8645a49bf4185c0c050e9fde21 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -2a730adc07b0a708b31dd8e592f56df4adbaf4be +dbc09d8230f0e273af8a78546e5431a7783478b5 The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index 42a9df518c94f0ca2af47b735f253412a805ca97..b7119d24b608ccca21f5dc46e34ec34d686eb82d 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -3735,6 +3735,47 @@ if (isInputRange!Range && !isInfinite!Range && assert(arr.minElement!"a.val".val == 0); } +// https://issues.dlang.org/show_bug.cgi?id=24827 +@safe unittest +{ + static struct S + { + int i; + bool destroyed; + + this(int i) @safe + { + this.i = i; + } + + ~this() @safe + { + destroyed = true; + } + + bool opEquals()(auto ref S rhs) + { + return this.i == rhs.i; + } + + int opCmp()(auto ref S rhs) + { + if (this.i < rhs.i) + return -1; + + return this.i == rhs.i ? 0 : 1; + } + + @safe invariant + { + assert(!destroyed); + } + } + + auto arr = [S(19), S(2), S(145), S(7)]; + assert(minElement(arr) == S(2)); +} + /** Iterates the passed range and returns the maximal element. A custom mapping function can be passed to `map`. @@ -3888,6 +3929,47 @@ if (isInputRange!Range && !isInfinite!Range && assert(arr[0].getI == 2); } +// https://issues.dlang.org/show_bug.cgi?id=24827 +@safe unittest +{ + static struct S + { + int i; + bool destroyed; + + this(int i) @safe + { + this.i = i; + } + + ~this() @safe + { + destroyed = true; + } + + bool opEquals()(auto ref S rhs) + { + return this.i == rhs.i; + } + + int opCmp()(auto ref S rhs) + { + if (this.i < rhs.i) + return -1; + + return this.i == rhs.i ? 0 : 1; + } + + @safe invariant + { + assert(!destroyed); + } + } + + auto arr = [S(19), S(2), S(145), S(7)]; + assert(maxElement(arr) == S(145)); +} + // minPos /** Computes a subrange of `range` starting at the first occurrence of `range`'s diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index acd5311c4d22cbe71268a166e7f9f3354a9b3b1f..3313dbb28d4492900bec0b56061dfad86a916c11 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -3639,6 +3639,7 @@ if (isDynamicArray!A) } else { + import core.stdc.string : memcpy, memset; // Time to reallocate. // We need to almost duplicate what's in druntime, except we // have better access to the capacity field. @@ -3650,6 +3651,15 @@ if (isDynamicArray!A) if (u) { // extend worked, update the capacity + // if the type has indirections, we need to zero any new + // data that we requested, as the existing data may point + // at large unused blocks. + static if (hasIndirections!T) + { + immutable addedSize = u - (_data.capacity * T.sizeof); + () @trusted { memset(_data.arr.ptr + _data.capacity, 0, addedSize); }(); + } + _data.capacity = u / T.sizeof; return; } @@ -3665,10 +3675,20 @@ if (isDynamicArray!A) auto bi = (() @trusted => GC.qalloc(nbytes, blockAttribute!T))(); _data.capacity = bi.size / T.sizeof; - import core.stdc.string : memcpy; if (len) () @trusted { memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }(); + _data.arr = (() @trusted => (cast(Unqual!T*) bi.base)[0 .. len])(); + + // we requested new bytes that are not in the existing + // data. If T has pointers, then this new data could point at stale + // objects from the last time this block was allocated. Zero that + // new data out, it may point at large unused blocks! + static if (hasIndirections!T) + () @trusted { + memset(bi.base + (len * T.sizeof), 0, (newlen - len) * T.sizeof); + }(); + _data.tryExtendBlock = true; // leave the old data, for safety reasons } @@ -4047,6 +4067,43 @@ if (isDynamicArray!A) app2.toString(); } +// https://issues.dlang.org/show_bug.cgi?id=24856 +@system unittest +{ + import core.memory : GC; + import std.stdio : writeln; + import std.algorithm.searching : canFind; + GC.disable(); + scope(exit) GC.enable(); + void*[] freeme; + // generate some poison blocks to allocate from. + auto poison = cast(void*) 0xdeadbeef; + foreach (i; 0 .. 10) + { + auto blk = new void*[7]; + blk[] = poison; + freeme ~= blk.ptr; + } + + foreach (p; freeme) + GC.free(p); + + int tests = 0; + foreach (i; 0 .. 10) + { + Appender!(void*[]) app; + app.put(null); + // if not a realloc of one of the deadbeef pointers, continue + if (!freeme.canFind(app.data.ptr)) + continue; + ++tests; + assert(!app.data.ptr[0 .. app.capacity].canFind(poison), "Appender not zeroing data!"); + } + // just notify in the log whether this test actually could be done. + if (tests == 0) + writeln("WARNING: test of Appender zeroing did not occur"); +} + //Calculates an efficient growth scheme based on the old capacity //of data, and the minimum requested capacity. //arg curLen: The current length diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d index 15211a3a98adab5ab2952bf801a350a5db76055c..f8a97dfbf19c989c1f57bdd610b4abd3de8523fc 100644 --- a/libphobos/src/std/bitmanip.d +++ b/libphobos/src/std/bitmanip.d @@ -106,7 +106,7 @@ private template createAccessors( enum RightShiftOp = ">>>="; } - static if (is(T == bool)) + static if (is(T : bool)) { enum createAccessors = // getter @@ -4676,3 +4676,24 @@ if (isIntegral!T) foreach (i; 0 .. 63) assert(bitsSet(1UL << i).equal([i])); } + +// Fix https://issues.dlang.org/show_bug.cgi?id=24095 +@safe @nogc pure unittest +{ + enum Bar : bool + { + a, + b, + } + + struct Foo + { + mixin(bitfields!(Bar, "bar", 1, ubyte, "", 7,)); + } + + Foo foo; + foo.bar = Bar.a; + assert(foo.bar == Bar.a); + foo.bar = Bar.b; + assert(foo.bar == Bar.b); +} diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d index 728aacde5c086dfcf14eb7df1ced86d751d75f63..8f7df105c834e05d1f54f3eb24afb3f5fd393952 100644 --- a/libphobos/src/std/container/dlist.d +++ b/libphobos/src/std/container/dlist.d @@ -185,6 +185,7 @@ Implements a doubly-linked list. struct DList(T) { import std.range : Take; + import std.traits : isMutable; /* A Node with a Payload. A PayNode. @@ -220,7 +221,10 @@ struct DList(T) { import std.algorithm.mutation : move; - return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode(); + static if (isMutable!Stuff) + return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode(); + else + return (new PayNode(BaseNode(prev, next), arg)).asBaseNode(); } void initialize() nothrow @safe pure @@ -1149,3 +1153,22 @@ private: list.removeFront(); assert(list[].walkLength == 0); } + +// https://issues.dlang.org/show_bug.cgi?id=24637 +@safe unittest +{ + import std.algorithm.comparison : equal; + + struct A + { + int c; + } + + DList!A B; + B.insert(A(1)); + assert(B[].equal([A(1)])); + + const a = A(3); + B.insert(a); + assert(B[].equal([A(1), A(3)])); +} diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 9c9d8db236a4e05c990931887d748d4dbe6031ea..5e0165cb60973a96422328fc68d2120004f9f8db 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -2560,9 +2560,6 @@ Lerr: string s1 = "123"; auto a1 = parse!(int, string, Yes.doCount)(s1); assert(a1.data == 123 && a1.count == 3); - - // parse only accepts lvalues - static assert(!__traits(compiles, parse!int("123"))); } /// @@ -5611,6 +5608,14 @@ Params: Returns: a `string`, a `wstring` or a `dstring`, according to the type of hexData. + +See_Also: + Use $(REF fromHexString, std, digest) for run time conversions. + Note, these functions are not drop-in replacements and have different + input requirements. + This template inherits its data syntax from builtin + $(LINK2 $(ROOT_DIR)spec/lex.html#hex_string, hex strings). + See $(REF fromHexString, std, digest) for its own respective requirements. */ template hexString(string hexData) if (hexData.isHexLiteral) diff --git a/libphobos/src/std/digest/package.d b/libphobos/src/std/digest/package.d index ea3738b2f82b88d1e22f4314eb0dcfd78a0904c0..8274680edaba3e64e358dff8030ca42eac3f2c65 100644 --- a/libphobos/src/std/digest/package.d +++ b/libphobos/src/std/digest/package.d @@ -1212,3 +1212,345 @@ if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && assert(!secureEqual(hex1, hex2)); } } + +/** + * Validates a hex string. + * + * Checks whether all characters following an optional "0x" suffix + * are valid hexadecimal digits. + * + * Params: + * hex = hexdecimal encoded byte array + * Returns: + * true = if valid + */ +bool isHexString(String)(String hex) @safe pure nothrow @nogc +if (isSomeString!String) +{ + import std.ascii : isHexDigit; + + if ((hex.length >= 2) && (hex[0 .. 2] == "0x")) + { + hex = hex[2 .. $]; + } + + foreach (digit; hex) + { + if (!digit.isHexDigit) + { + return false; + } + } + + return true; +} + +/// +@safe unittest +{ + assert(isHexString("0x0123456789ABCDEFabcdef")); + assert(isHexString("0123456789ABCDEFabcdef")); + assert(!isHexString("g")); + assert(!isHexString("#")); +} + +/** + * Converts a hex text string to a range of bytes. + * + * The input to this function MUST be valid. + * $(REF isHexString, std, digest) can be used to check for this if needed. + * + * Params: + * hex = String representation of a hexdecimal-encoded byte array. + * Returns: + * A forward range of bytes. + */ +auto fromHexStringAsRange(String)(String hex) @safe pure nothrow @nogc +if (isSomeString!String) +{ + return HexStringDecoder!String(hex); +} + +/// +@safe unittest +{ + import std.range.primitives : ElementType, isForwardRange; + import std.traits : ReturnType; + + // The decoder implements a forward range. + static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!string))); + static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!wstring))); + static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!dstring))); + + // The element type of the range is always `ubyte`. + static assert( + is(ElementType!(ReturnType!(fromHexStringAsRange!string)) == ubyte) + ); + static assert( + is(ElementType!(ReturnType!(fromHexStringAsRange!wstring)) == ubyte) + ); + static assert( + is(ElementType!(ReturnType!(fromHexStringAsRange!dstring)) == ubyte) + ); +} + +@safe unittest +{ + import std.array : staticArray; + + // `staticArray` consumes the range returned by `fromHexStringAsRange`. + assert("0x0000ff".fromHexStringAsRange.staticArray!3 == [0, 0, 0xFF]); + assert("0x0000ff"w.fromHexStringAsRange.staticArray!3 == [0, 0, 0xFF]); + assert("0x0000ff"d.fromHexStringAsRange.staticArray!3 == [0, 0, 0xFF]); + assert("0xff12ff".fromHexStringAsRange.staticArray!1 == [0xFF]); + assert("0x12ff".fromHexStringAsRange.staticArray!2 == [0x12, 255]); + assert( + "0x3AaAA".fromHexStringAsRange.staticArray!4 == [0x3, 0xAA, 0xAA, 0x00] + ); +} + +/** + * Converts a hex text string to a range of bytes. + * + * Params: + * hex = String representation of a hexdecimal-encoded byte array. + * Returns: + * An newly allocated array of bytes. + * Throws: + * Exception on invalid input. + * Example: + * --- + * ubyte[] dby = "0xBA".fromHexString; + * --- + * See_Also: + * $(REF fromHexString, std, digest) for a range version of the function. + */ +ubyte[] fromHexString(String)(String hex) @safe pure +if (isSomeString!String) +{ + // This function is trivial, yet necessary for consistency. + // It provides a similar API to its `toHexString` counterpart. + + if (!hex.isHexString) + { + import std.conv : text; + + throw new Exception( + "The provided character sequence `" + ~ hex.text + ~ "` is not a valid hex string." + ); + } + + if ((hex.length >= 2) && (hex[0 .. 2] == "0x")) + { + hex = hex[2 .. $]; + } + + auto decoder = HexStringDecoder!String(hex); + auto result = new ubyte[](decoder.length); + + size_t idx = 0; + foreach (b; decoder) + { + result[idx++] = b; + } + return result; +} + +/// +@safe unittest +{ + // Single byte + assert("0xff".fromHexString == [255]); + assert("0xff"w.fromHexString == [255]); + assert("0xff"d.fromHexString == [255]); + assert("0xC0".fromHexString == [192]); + assert("0x00".fromHexString == [0]); + + // Nothing + assert("".fromHexString == []); + assert(""w.fromHexString == []); + assert(""d.fromHexString == []); + + // Nothing but a prefix + assert("0x".fromHexString == []); + assert("0x"w.fromHexString == []); + assert("0x"d.fromHexString == []); + + // Half a byte + assert("0x1".fromHexString == [0x01]); + assert("0x1"w.fromHexString == [0x01]); + assert("0x1"d.fromHexString == [0x01]); + + // Mixed case is fine. + assert("0xAf".fromHexString == [0xAF]); + assert("0xaF".fromHexString == [0xAF]); + + // Multiple bytes + assert("0xfff".fromHexString == [0x0F, 0xFF]); + assert("0x123AaAa".fromHexString == [0x01, 0x23, 0xAA, 0xAA]); + assert("EBBBBF".fromHexString == [0xEB, 0xBB, 0xBF]); + + // md5 sum + assert("d41d8cd98f00b204e9800998ecf8427e".fromHexString == [ + 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, + 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E, + ]); +} + +/// +@safe unittest +{ + // Cycle self-test + const ubyte[] initial = [0x00, 0x12, 0x34, 0xEB]; + assert(initial == initial.toHexString().fromHexString()); +} + +private ubyte hexDigitToByte(dchar hexDigit) @safe pure nothrow @nogc +{ + static int hexDigitToByteImpl(dchar hexDigit) + { + if (hexDigit >= '0' && hexDigit <= '9') + { + return hexDigit - '0'; + } + else if (hexDigit >= 'A' && hexDigit <= 'F') + { + return hexDigit - 'A' + 10; + } + else if (hexDigit >= 'a' && hexDigit <= 'f') + { + return hexDigit - 'a' + 10; + } + + assert(false, "Cannot convert invalid hex digit."); + } + + return hexDigitToByteImpl(hexDigit) & 0xFF; +} + +@safe unittest +{ + assert(hexDigitToByte('0') == 0x0); + assert(hexDigitToByte('9') == 0x9); + assert(hexDigitToByte('a') == 0xA); + assert(hexDigitToByte('b') == 0xB); + assert(hexDigitToByte('A') == 0xA); + assert(hexDigitToByte('C') == 0xC); +} + +private struct HexStringDecoder(String) +if (isSomeString!String) +{ + String hex; + ubyte front; + bool empty; + + this(String hex) + { + if ((hex.length >= 2) && (hex[0 .. 2] == "0x")) + { + hex = hex[2 .. $]; + } + + if (hex.length == 0) + { + empty = true; + return; + } + + const oddInputLength = (hex.length % 2 == 1); + + if (oddInputLength) + { + front = hexDigitToByte(hex[0]); + hex = hex[1 .. $]; + } + else + { + front = cast(ubyte)(hexDigitToByte(hex[0]) << 4 | hexDigitToByte(hex[1])); + hex = hex[2 .. $]; + } + + this.hex = hex; + } + + void popFront() + { + if (hex.length == 0) + { + empty = true; + return; + } + + front = cast(ubyte)(hexDigitToByte(hex[0]) << 4 | hexDigitToByte(hex[1])); + hex = hex[2 .. $]; + } + + typeof(this) save() + { + return this; + } + + size_t length() const + { + if (this.empty) + { + return 0; + } + + // current front + remainder + return 1 + (hex.length >> 1); + } +} + +@safe unittest +{ + auto decoder = HexStringDecoder!string(""); + assert(decoder.empty); + assert(decoder.length == 0); + + decoder = HexStringDecoder!string("0x"); + assert(decoder.empty); + assert(decoder.length == 0); +} + +@safe unittest +{ + auto decoder = HexStringDecoder!string("0x0077FF"); + assert(!decoder.empty); + assert(decoder.length == 3); + assert(decoder.front == 0x00); + + decoder.popFront(); + assert(!decoder.empty); + assert(decoder.length == 2); + assert(decoder.front == 0x77); + + decoder.popFront(); + assert(!decoder.empty); + assert(decoder.length == 1); + assert(decoder.front == 0xFF); + + decoder.popFront(); + assert(decoder.length == 0); + assert(decoder.empty); +} + +@safe unittest +{ + auto decoder = HexStringDecoder!string("0x7FF"); + assert(!decoder.empty); + assert(decoder.length == 2); + assert(decoder.front == 0x07); + + decoder.popFront(); + assert(!decoder.empty); + assert(decoder.length == 1); + assert(decoder.front == 0xFF); + + decoder.popFront(); + assert(decoder.length == 0); + assert(decoder.empty); +} diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d index 8b60565d8a17177b2bfd83da430d98dcb793e11f..6fd468d315e6172c84acd6b755461d3ca9f11db4 100644 --- a/libphobos/src/std/format/internal/write.d +++ b/libphobos/src/std/format/internal/write.d @@ -1839,24 +1839,26 @@ template hasToString(T, Char) else static if (is(typeof( (T val) { const FormatSpec!Char f; - static struct S {void put(scope Char s){}} + static struct S + { + @disable this(this); + void put(scope Char s){} + } S s; val.toString(s, f); - static assert(!__traits(compiles, val.toString(s, FormatSpec!Char())), - "force toString to take parameters by ref"); - static assert(!__traits(compiles, val.toString(S(), f)), - "force toString to take parameters by ref"); }))) { enum hasToString = HasToStringResult.customPutWriterFormatSpec; } else static if (is(typeof( (T val) { - static struct S {void put(scope Char s){}} + static struct S + { + @disable this(this); + void put(scope Char s){} + } S s; val.toString(s); - static assert(!__traits(compiles, val.toString(S())), - "force toString to take parameters by ref"); }))) { enum hasToString = HasToStringResult.customPutWriter; @@ -1996,9 +1998,10 @@ template hasToString(T, Char) static assert(hasToString!(G, char) == customPutWriter); static assert(hasToString!(H, char) == customPutWriterFormatSpec); static assert(hasToString!(I, char) == customPutWriterFormatSpec); - static assert(hasToString!(J, char) == hasSomeToString); + static assert(hasToString!(J, char) == hasSomeToString + || hasToString!(J, char) == constCharSinkFormatSpec); // depends on -preview=rvaluerefparam static assert(hasToString!(K, char) == constCharSinkFormatSpec); - static assert(hasToString!(L, char) == none); + static assert(hasToString!(L, char) == customPutWriterFormatSpec); static if (hasPreviewIn) { static assert(hasToString!(M, char) == inCharSinkFormatSpec); @@ -2105,9 +2108,10 @@ template hasToString(T, Char) static assert(hasToString!(G, char) == customPutWriter); static assert(hasToString!(H, char) == customPutWriterFormatSpec); static assert(hasToString!(I, char) == customPutWriterFormatSpec); - static assert(hasToString!(J, char) == hasSomeToString); + static assert(hasToString!(J, char) == hasSomeToString + || hasToString!(J, char) == constCharSinkFormatSpec); // depends on -preview=rvaluerefparam static assert(hasToString!(K, char) == constCharSinkFormatSpec); - static assert(hasToString!(L, char) == none); + static assert(hasToString!(L, char) == HasToStringResult.customPutWriterFormatSpec); static if (hasPreviewIn) { static assert(hasToString!(M, char) == inCharSinkFormatSpec); @@ -2125,9 +2129,10 @@ template hasToString(T, Char) static assert(hasToString!(inout(G), char) == customPutWriter); static assert(hasToString!(inout(H), char) == customPutWriterFormatSpec); static assert(hasToString!(inout(I), char) == customPutWriterFormatSpec); - static assert(hasToString!(inout(J), char) == hasSomeToString); + static assert(hasToString!(inout(J), char) == hasSomeToString + || hasToString!(inout(J), char) == constCharSinkFormatSpec); // depends on -preview=rvaluerefparam static assert(hasToString!(inout(K), char) == constCharSinkFormatSpec); - static assert(hasToString!(inout(L), char) == none); + static assert(hasToString!(inout(L), char) == customPutWriterFormatSpec); static if (hasPreviewIn) { static assert(hasToString!(inout(M), char) == inCharSinkFormatSpec); diff --git a/libphobos/src/std/format/read.d b/libphobos/src/std/format/read.d index da9d0dc14db4337eb77c4d5cf31460098ff6b3ce..e2f9b944a18aeef57c5d7cd703d91b394348d417 100644 --- a/libphobos/src/std/format/read.d +++ b/libphobos/src/std/format/read.d @@ -198,7 +198,8 @@ module std.format.read; import std.format.spec : FormatSpec; import std.format.internal.read; -import std.traits : isSomeString; +import std.meta : allSatisfy; +import std.traits : isSomeString, isType; /** Reads an input range according to a format string and stores the read @@ -300,7 +301,7 @@ uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, au /// ditto uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args) -if (isSomeString!(typeof(fmt))) +if (!isType!fmt && isSomeString!(typeof(fmt))) { import std.format : checkFormatException; import std.meta : staticMap; @@ -692,6 +693,116 @@ if (isSomeString!(typeof(fmt))) assert(aa2 == ["hello":1, "world":2]); } +/** +Reads an input range according to a format string and returns a tuple of Args +with the read values. + +Format specifiers with format character $(B 'd'), $(B 'u') and $(B +'c') can take a $(B '*') parameter for skipping values. + +The second version of `formattedRead` takes the format string as +template argument. In this case, it is checked for consistency at +compile-time. + +Params: + Args = a variadic list of types of the arguments + */ +template formattedRead(Args...) +if (Args.length && allSatisfy!(isType, Args)) +{ + import std.typecons : Tuple; + + /** + Params: + r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives), + where the formatted input is read from + fmt = a $(MREF_ALTTEXT format string, std,format) + Range = the type of the input range `r` + Char = the character type used for `fmt` + + Returns: + A Tuple!Args with the elements filled. + + Throws: + A $(REF_ALTTEXT FormatException, FormatException, std, format) + if reading did not succeed. + */ + Tuple!Args formattedRead(Range, Char)(auto ref Range r, const(Char)[] fmt) + { + import core.lifetime : forward; + import std.format : enforceFmt; + + Tuple!Args args; + const numArgsFilled = .formattedRead(forward!r, fmt, args.expand); + enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments"); + return args; + } +} + +/// +@safe pure unittest +{ + import std.exception : assertThrown; + import std.format : FormatException; + import std.typecons : tuple; + + auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s"); + assert(complete == tuple("hello", 34.5, 124)); + + // reading ends early + assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s")); +} + +/// Skipping values +@safe pure unittest +{ + import std.format : FormatException; + import std.typecons : tuple; + + auto result = "orange: (12%) 15.25".formattedRead!(string, double)("%s: (%*d%%) %f"); + assert(result == tuple("orange", 15.25)); +} + +/// ditto +template formattedRead(alias fmt, Args...) +if (!isType!fmt && isSomeString!(typeof(fmt)) && Args.length && allSatisfy!(isType, Args)) +{ + import std.typecons : Flag, Tuple, Yes; + Tuple!Args formattedRead(Range)(auto ref Range r) + { + import core.lifetime : forward; + import std.format : enforceFmt; + + Tuple!Args args; + const numArgsFilled = .formattedRead!fmt(forward!r, args.expand); + enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments"); + return args; + } +} + +/// The format string can be checked at compile-time +@safe pure unittest +{ + import std.exception : assertThrown; + import std.format : FormatException; + import std.typecons : tuple; + + auto expected = tuple("hello", 124, 34.5); + auto result = "hello!124:34.5".formattedRead!("%s!%s:%s", string, int, double); + assert(result == expected); + + assertThrown!FormatException("hello!34.5:".formattedRead!("%s!%s:%s", string, double, int)); +} + +/// Compile-time consistency check +@safe pure unittest +{ + import std.format : FormatException; + import std.typecons : tuple; + + static assert(!__traits(compiles, "orange: (12%) 15.25".formattedRead!("%s: (%*d%%) %f", string, double))); +} + /** Reads a value from the given _input range and converts it according to a format specifier. diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d index cc938d4fd34970d09bbbc9faeeb93b1317f4d86f..1e879fd6c56f7b1a85f61381cfb0d0eafec8ff85 100644 --- a/libphobos/src/std/logger/core.d +++ b/libphobos/src/std/logger/core.d @@ -1433,7 +1433,7 @@ logger by the user, the default logger's log level is LogLevel.info. Example: ------------- -sharedLog = new FileLogger(yourFile); +sharedLog = new shared FileLogger(yourFile); ------------- The example sets a new `FileLogger` as new `sharedLog`. @@ -1450,7 +1450,7 @@ writing `sharedLog`. The default `Logger` is thread-safe. ------------- if (sharedLog !is myLogger) - sharedLog = new myLogger; + sharedLog = new shared myLogger; ------------- */ @property shared(Logger) sharedLog() @safe diff --git a/libphobos/src/std/logger/filelogger.d b/libphobos/src/std/logger/filelogger.d index c662ca74e30059fa8fee82b14070ca729d826c4b..5ba167c7bdb368734caad9e1f4535dd066a73989 100644 --- a/libphobos/src/std/logger/filelogger.d +++ b/libphobos/src/std/logger/filelogger.d @@ -37,7 +37,7 @@ class FileLogger : Logger auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes); ------------- */ - this(const string fn, const LogLevel lv = LogLevel.all) @safe + this(this This)(const string fn, const LogLevel lv = LogLevel.all) { this(fn, lv, CreateFolder.yes); } @@ -63,7 +63,7 @@ class FileLogger : Logger auto l2 = new FileLogger(file, LogLevel.fatal); ------------- */ - this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe + this(this This)(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) { import std.file : exists, mkdirRecurse; import std.path : dirName; @@ -80,7 +80,8 @@ class FileLogger : Logger " created in '", d,"' could not be created.")); } - this.file_.open(this.filename, "a"); + // Cast away `shared` when the constructor is inferred shared. + () @trusted { (cast() this.file_).open(this.filename, "a"); }(); } /** A constructor for the `FileLogger` Logger that takes a reference to @@ -270,3 +271,12 @@ class FileLogger : Logger assert(tl !is null); stdThreadLocalLog.logLevel = LogLevel.all; } + +@safe unittest +{ + // we don't need to actually run the code, only make sure + // it compiles + static _() { + auto l = new shared FileLogger(""); + } +} diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d index 14a439486dba864361f613329eaa7ce790f89a4f..215ca20b8772d76bd94c6ade717cc8aac559d69b 100644 --- a/libphobos/src/std/logger/package.d +++ b/libphobos/src/std/logger/package.d @@ -64,7 +64,7 @@ using the property called `sharedLog`. This property is a reference to the current default `Logger`. This reference can be used to assign a new default `Logger`. ------------- -sharedLog = new FileLogger("New_Default_Log_File.log"); +sharedLog = new shared FileLogger("New_Default_Log_File.log"); ------------- Additional `Logger` can be created by creating a new instance of the diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d index 3fef8e4f1feaa9a5b08951886080575e38604124..9966b1ce77fcdf3d8cbcbd82ef05fca1320bb413 100644 --- a/libphobos/src/std/numeric.d +++ b/libphobos/src/std/numeric.d @@ -223,7 +223,7 @@ private: } // Convert the current value to signed exponent, normalized form - void toNormalized(T,U)(ref T sig, ref U exp) + void toNormalized(T,U)(ref T sig, ref U exp) const { sig = significand; auto shift = (T.sizeof*8) - precision; @@ -490,7 +490,7 @@ public: } /// Returns: real part - @property CustomFloat re() { return this; } + @property CustomFloat re() const { return this; } /// Returns: imaginary part static @property CustomFloat im() { return CustomFloat(0.0f); } @@ -546,7 +546,7 @@ public: } /// Fetches the stored value either as a `float`, `double` or `real`. - @property F get(F)() + @property F get(F)() const if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) { import std.conv : text; @@ -591,14 +591,14 @@ public: // Define an opBinary `CustomFloat op CustomFloat` so that those below // do not match equally, which is disallowed by the spec: // https://dlang.org/spec/operatoroverloading.html#binary - real opBinary(string op,T)(T b) + real opBinary(string op,T)(T b) const if (__traits(compiles, mixin(`get!real`~op~`b.get!real`))) { return mixin(`get!real`~op~`b.get!real`); } /// ditto - real opBinary(string op,T)(T b) + real opBinary(string op,T)(T b) const if ( __traits(compiles, mixin(`get!real`~op~`b`)) && !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) { @@ -606,7 +606,7 @@ public: } /// ditto - real opBinaryRight(string op,T)(T a) + real opBinaryRight(string op,T)(T a) const if ( __traits(compiles, mixin(`a`~op~`get!real`)) && !__traits(compiles, mixin(`get!real`~op~`b`)) && !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) @@ -615,7 +615,7 @@ public: } /// ditto - int opCmp(T)(auto ref T b) + int opCmp(T)(auto ref T b) const if (__traits(compiles, cast(real) b)) { auto x = get!real; @@ -949,6 +949,17 @@ public: assertThrown!AssertError(a = float.infinity); } +@safe unittest +{ + const CustomFloat!16 x = CustomFloat!16(3); + assert(x.get!float == 3); + assert(x.re.get!float == 3); + assert(x + x == 6); + assert(x + 1 == 4); + assert(2 + x == 5); + assert(x < 4); +} + private bool isCorrectCustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags) @safe pure nothrow @nogc { // Restrictions from bitfield diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 4f593bd067fe4970fc4eee0ec7f9b02d5c43e0ce..2efbcaa84c8cd70e3e405529cc6f548c83faf889 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -4631,11 +4631,12 @@ else version (Posix) if (childpid == 0) { // Trusted because args and all entries are always zero-terminated - (() @trusted => - core.sys.posix.unistd.execvp(args[0], &args[0]) || - perror(args[0]) // failed to execute - )(); - return; + (() @trusted { + core.sys.posix.unistd.execvp(args[0], &args[0]); + perror(args[0]); + core.sys.posix.unistd._exit(1); + })(); + assert(0, "Child failed to exec"); } if (browser) // Trusted because it's allocated via strdup above diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d index 52fd33b1dd1e32e0b0ebf7c52ed9b2ee95b140fd..7fa9974189f66dd8d484e0faae7b838edff7adb8 100644 --- a/libphobos/src/std/socket.d +++ b/libphobos/src/std/socket.d @@ -54,6 +54,12 @@ version (Windows) enum socket_t : SOCKET { INVALID_SOCKET } private const int _SOCKET_ERROR = SOCKET_ERROR; + /** + * On Windows, there is no `SO_REUSEPORT`. + * However, `SO_REUSEADDR` is equivalent to `SO_REUSEPORT` there. + * $(LINK https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) + */ + private enum SO_REUSEPORT = SO_REUSEADDR; private int _lasterr() nothrow @nogc { @@ -2589,6 +2595,22 @@ enum SocketOption: int DEBUG = SO_DEBUG, /// Record debugging information BROADCAST = SO_BROADCAST, /// Allow transmission of broadcast messages REUSEADDR = SO_REUSEADDR, /// Allow local reuse of address + /** + * Allow local reuse of port + * + * On Windows, this is equivalent to `SocketOption.REUSEADDR`. + * There is in fact no option named `REUSEPORT`. + * However, `SocketOption.REUSEADDR` matches the behavior of + * `SocketOption.REUSEPORT` on other platforms. Further details on this + * topic can be found here: + * $(LINK https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) + * + * On Linux, this ensures fair distribution of incoming connections accross threads. + * + * See_Also: + * https://lwn.net/Articles/542629/ + */ + REUSEPORT = SO_REUSEPORT, LINGER = SO_LINGER, /// Linger on close if unsent data is present OOBINLINE = SO_OOBINLINE, /// Receive out-of-band data in band SNDBUF = SO_SNDBUF, /// Send buffer size diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d index 69c2a49dd7faed5b62c00e69ccb9eb6beaf78d62..ad2942873a650a0f0446cb6ceb975b7198d78df7 100644 --- a/libphobos/src/std/sumtype.d +++ b/libphobos/src/std/sumtype.d @@ -1860,88 +1860,65 @@ private template Iota(size_t n) assert(Iota!3 == AliasSeq!(0, 1, 2)); } -/* The number that the dim-th argument's tag is multiplied by when - * converting TagTuples to and from case indices ("caseIds"). - * - * Named by analogy to the stride that the dim-th index into a - * multidimensional static array is multiplied by to calculate the - * offset of a specific element. - */ -private size_t stride(size_t dim, lengths...)() -{ - import core.checkedint : mulu; - - size_t result = 1; - bool overflow = false; - - static foreach (i; 0 .. dim) - { - result = mulu(result, lengths[i], overflow); - } - - /* The largest number matchImpl uses, numCases, is calculated with - * stride!(SumTypes.length), so as long as this overflow check - * passes, we don't need to check for overflow anywhere else. - */ - assert(!overflow, "Integer overflow"); - return result; -} - private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) { auto ref matchImpl(SumTypes...)(auto ref SumTypes args) if (allSatisfy!(isSumType, SumTypes) && args.length > 0) { - alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); - alias TagTuple = .TagTuple!(SumTypes); - - /* - * A list of arguments to be passed to a handler needed for the case - * labeled with `caseId`. - */ - template handlerArgs(size_t caseId) + // Single dispatch (fast path) + static if (args.length == 1) { - enum tags = TagTuple.fromCaseId(caseId); - enum argsFrom(size_t i : tags.length) = ""; - enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~ - ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1); - enum handlerArgs = argsFrom!0; - } + /* When there's only one argument, the caseId is just that + * argument's tag, so there's no need for TagTuple. + */ + enum handlerArgs(size_t caseId) = + "args[0].get!(SumTypes[0].Types[" ~ toCtString!caseId ~ "])()"; - /* An AliasSeq of the types of the member values in the argument list - * returned by `handlerArgs!caseId`. - * - * Note that these are the actual (that is, qualified) types of the - * member values, which may not be the same as the types listed in - * the arguments' `.Types` properties. - */ - template valueTypes(size_t caseId) + alias valueTypes(size_t caseId) = + typeof(args[0].get!(SumTypes[0].Types[caseId])()); + + enum numCases = SumTypes[0].Types.length; + } + // Multiple dispatch (slow path) + else { - enum tags = TagTuple.fromCaseId(caseId); + alias typeCounts = Map!(typeCount, SumTypes); + alias stride(size_t i) = .stride!(i, typeCounts); + alias TagTuple = .TagTuple!typeCounts; + + alias handlerArgs(size_t caseId) = .handlerArgs!(caseId, typeCounts); - template getType(size_t i) + /* An AliasSeq of the types of the member values in the argument list + * returned by `handlerArgs!caseId`. + * + * Note that these are the actual (that is, qualified) types of the + * member values, which may not be the same as the types listed in + * the arguments' `.Types` properties. + */ + template valueTypes(size_t caseId) { - enum tid = tags[i]; - alias T = SumTypes[i].Types[tid]; - alias getType = typeof(args[i].get!T()); + enum tags = TagTuple.fromCaseId(caseId); + + template getType(size_t i) + { + enum tid = tags[i]; + alias T = SumTypes[i].Types[tid]; + alias getType = typeof(args[i].get!T()); + } + + alias valueTypes = Map!(getType, Iota!(tags.length)); } - alias valueTypes = Map!(getType, Iota!(tags.length)); + /* The total number of cases is + * + * Î SumTypes[i].Types.length for 0 ≤ i < SumTypes.length + * + * Conveniently, this is equal to stride!(SumTypes.length), so we can + * use that function to compute it. + */ + enum numCases = stride!(SumTypes.length); } - /* The total number of cases is - * - * Î SumTypes[i].Types.length for 0 ≤ i < SumTypes.length - * - * Or, equivalently, - * - * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof - * - * Conveniently, this is equal to stride!(SumTypes.length), so we can - * use that function to compute it. - */ - enum numCases = stride!(SumTypes.length); - /* Guaranteed to never be a valid handler index, since * handlers.length <= size_t.max. */ @@ -1998,7 +1975,12 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) mixin("alias ", handlerName!hid, " = handler;"); } - immutable argsId = TagTuple(args).toCaseId; + // Single dispatch (fast path) + static if (args.length == 1) + immutable argsId = args[0].tag; + // Multiple dispatch (slow path) + else + immutable argsId = TagTuple(args).toCaseId; final switch (argsId) { @@ -2029,10 +2011,11 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) } } +// Predicate for staticMap private enum typeCount(SumType) = SumType.Types.length; -/* A TagTuple represents a single possible set of tags that `args` - * could have at runtime. +/* A TagTuple represents a single possible set of tags that the arguments to + * `matchImpl` could have at runtime. * * Because D does not allow a struct to be the controlling expression * of a switch statement, we cannot dispatch on the TagTuple directly. @@ -2054,22 +2037,23 @@ private enum typeCount(SumType) = SumType.Types.length; * When there is only one argument, the caseId is equal to that * argument's tag. */ -private struct TagTuple(SumTypes...) +private struct TagTuple(typeCounts...) { - size_t[SumTypes.length] tags; + size_t[typeCounts.length] tags; alias tags this; - alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); + alias stride(size_t i) = .stride!(i, typeCounts); invariant { static foreach (i; 0 .. tags.length) { - assert(tags[i] < SumTypes[i].Types.length, "Invalid tag"); + assert(tags[i] < typeCounts[i], "Invalid tag"); } } - this(ref const(SumTypes) args) + this(SumTypes...)(ref const SumTypes args) + if (allSatisfy!(isSumType, SumTypes) && args.length == typeCounts.length) { static foreach (i; 0 .. tags.length) { @@ -2104,6 +2088,52 @@ private struct TagTuple(SumTypes...) } } +/* The number that the dim-th argument's tag is multiplied by when + * converting TagTuples to and from case indices ("caseIds"). + * + * Named by analogy to the stride that the dim-th index into a + * multidimensional static array is multiplied by to calculate the + * offset of a specific element. + */ +private size_t stride(size_t dim, lengths...)() +{ + import core.checkedint : mulu; + + size_t result = 1; + bool overflow = false; + + static foreach (i; 0 .. dim) + { + result = mulu(result, lengths[i], overflow); + } + + /* The largest number matchImpl uses, numCases, is calculated with + * stride!(SumTypes.length), so as long as this overflow check + * passes, we don't need to check for overflow anywhere else. + */ + assert(!overflow, "Integer overflow"); + return result; +} + +/* A list of arguments to be passed to a handler needed for the case + * labeled with `caseId`. + */ +private template handlerArgs(size_t caseId, typeCounts...) +{ + enum tags = TagTuple!typeCounts.fromCaseId(caseId); + + alias handlerArgs = AliasSeq!(); + + static foreach (i; 0 .. tags.length) + { + handlerArgs = AliasSeq!( + handlerArgs, + "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~ + ".Types[" ~ toCtString!(tags[i]) ~ "])(), " + ); + } +} + // Matching @safe unittest { diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index 69362c08695dd9d645a58232a705ad2db51f05ef..f230aa3b74e43a006243d3983ab705dd0aadc139 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -7251,16 +7251,21 @@ alias PointerTarget(T : T*) = T; /** * Detect whether type `T` is an aggregate type. */ -enum bool isAggregateType(T) = is(T == struct) || is(T == union) || - is(T == class) || is(T == interface); +template isAggregateType(T) +{ + static if (is(T == enum)) + enum isAggregateType = isAggregateType!(OriginalType!T); + else + enum isAggregateType = is(T == struct) || is(T == class) || is(T == interface) || is(T == union); +} /// @safe unittest { - class C; - union U; - struct S; - interface I; + class C {} + union U {} + struct S {} + interface I {} static assert( isAggregateType!C); static assert( isAggregateType!U); @@ -7271,6 +7276,16 @@ enum bool isAggregateType(T) = is(T == struct) || is(T == union) || static assert(!isAggregateType!(int[])); static assert(!isAggregateType!(C[string])); static assert(!isAggregateType!(void delegate(int))); + + enum ES : S { a = S.init } + enum EC : C { a = C.init } + enum EI : I { a = I.init } + enum EU : U { a = U.init } + + static assert( isAggregateType!ES); + static assert( isAggregateType!EC); + static assert( isAggregateType!EI); + static assert( isAggregateType!EU); } /** @@ -9238,12 +9253,16 @@ enum isCopyable(S) = __traits(isCopyable, S); * is the same as `T`. For pointer and slice types, it is `T` with the * outer-most layer of qualifiers dropped. */ -package(std) template DeducedParameterType(T) +package(std) alias DeducedParameterType(T) = DeducedParameterTypeImpl!T; +/// ditto +package(std) alias DeducedParameterType(alias T) = DeducedParameterTypeImpl!T; + +private template DeducedParameterTypeImpl(T) { static if (is(T == U*, U) || is(T == U[], U)) - alias DeducedParameterType = Unqual!T; + alias DeducedParameterTypeImpl = Unqual!T; else - alias DeducedParameterType = T; + alias DeducedParameterTypeImpl = T; } @safe unittest @@ -9263,6 +9282,7 @@ package(std) template DeducedParameterType(T) } static assert(is(DeducedParameterType!NoCopy == NoCopy)); + static assert(is(DeducedParameterType!(inout(NoCopy)) == inout(NoCopy))); } @safe unittest diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index c874c0ff81166b822773efbdcc046d6232b050fc..bd462f53a5b1c8119cfd9f9923bcb5ad27114f51 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -3104,17 +3104,18 @@ private: { static if (useQualifierCast) { - this.data = cast() value; + static if (hasElaborateAssign!T) + { + import core.lifetime : copyEmplace; + copyEmplace(cast() value, this.data); + } + else + this.data = cast() value; } else { - // As we're escaping a copy of `value`, deliberately leak a copy: - static union DontCallDestructor - { - T value; - } - DontCallDestructor copy = DontCallDestructor(value); - this.data = *cast(Payload*) © + import core.lifetime : copyEmplace; + copyEmplace(cast() value, cast() *cast(T*) &this.data); } } @@ -3139,6 +3140,334 @@ package(std) Rebindable2!T rebindable2(T)(T value) return Rebindable2!T(value); } +// Verify that the destructor is called properly if there is one. +@system unittest +{ + { + bool destroyed; + + struct S + { + int i; + + this(int i) @safe + { + this.i = i; + } + + ~this() @safe + { + destroyed = true; + } + } + + { + auto foo = rebindable2(S(42)); + + // Whether destruction has occurred here depends on whether the + // temporary gets moved or not, so we won't assume that it has or + // hasn't happened. What we care about here is that foo gets destroyed + // properly when it leaves the scope. + destroyed = false; + } + assert(destroyed); + + { + auto foo = rebindable2(const S(42)); + destroyed = false; + } + assert(destroyed); + } + + // Test for double destruction with qualifer cast being used + { + static struct S + { + int i; + bool destroyed; + + this(int i) @safe + { + this.i = i; + } + + ~this() @safe + { + destroyed = true; + } + + @safe invariant + { + assert(!destroyed); + } + } + + { + auto foo = rebindable2(S(42)); + assert(typeof(foo).useQualifierCast); + assert(foo.data.i == 42); + assert(!foo.data.destroyed); + } + { + auto foo = rebindable2(S(42)); + destroy(foo); + } + { + auto foo = rebindable2(const S(42)); + assert(typeof(foo).useQualifierCast); + assert(foo.data.i == 42); + assert(!foo.data.destroyed); + } + { + auto foo = rebindable2(const S(42)); + destroy(foo); + } + } + + // Test for double destruction without qualifer cast being used + { + static struct S + { + int i; + bool destroyed; + + this(int i) @safe + { + this.i = i; + } + + ~this() @safe + { + destroyed = true; + } + + @disable ref S opAssign()(auto ref S rhs); + + @safe invariant + { + assert(!destroyed); + } + } + + { + auto foo = rebindable2(S(42)); + assert(!typeof(foo).useQualifierCast); + assert((cast(S*)&(foo.data)).i == 42); + assert(!(cast(S*)&(foo.data)).destroyed); + } + { + auto foo = rebindable2(S(42)); + destroy(foo); + } + } +} + +// Verify that if there is an overloaded assignment operator, it's not assigned +// to garbage. +@safe unittest +{ + static struct S + { + int i; + bool destroyed; + + this(int i) @safe + { + this.i = i; + } + + ~this() @safe + { + destroyed = true; + } + + ref opAssign()(auto ref S rhs) + { + assert(!this.destroyed); + this.i = rhs.i; + return this; + } + } + + { + auto foo = rebindable2(S(42)); + foo = S(99); + assert(foo.data.i == 99); + } + { + auto foo = rebindable2(S(42)); + foo = const S(99); + assert(foo.data.i == 99); + } +} + +// Verify that postblit or copy constructor is called properly if there is one. +@system unittest +{ + // postblit with type qualifier cast + { + static struct S + { + int i; + static bool copied; + + this(this) @safe + { + copied = true; + } + } + + { + auto foo = rebindable2(S(42)); + + // Whether a copy has occurred here depends on whether the + // temporary gets moved or not, so we won't assume that it has or + // hasn't happened. What we care about here is that foo gets copied + // properly when we copy it below. + S.copied = false; + + auto bar = foo; + assert(S.copied); + } + { + auto foo = rebindable2(const S(42)); + assert(typeof(foo).useQualifierCast); + S.copied = false; + + auto bar = foo; + assert(S.copied); + } + } + + // copy constructor with type qualifier cast + { + static struct S + { + int i; + static bool copied; + + this(ref inout S rhs) @safe inout + { + this.i = i; + copied = true; + } + } + + { + auto foo = rebindable2(S(42)); + assert(typeof(foo).useQualifierCast); + S.copied = false; + + auto bar = foo; + assert(S.copied); + } + { + auto foo = rebindable2(const S(42)); + S.copied = false; + + auto bar = foo; + assert(S.copied); + } + } + + // FIXME https://issues.dlang.org/show_bug.cgi?id=24829 + + // Making this work requires either reworking how the !useQualiferCast + // version works so that the compiler can correctly generate postblit + // constructors and copy constructors as appropriate, or an explicit + // postblit or copy constructor needs to be added for such cases, which + // gets pretty complicated if we want to correctly add the same attributes + // that T's postblit or copy constructor has. + + /+ + // postblit without type qualifier cast + { + static struct S + { + int* ptr; + static bool copied; + + this(int i) + { + ptr = new int(i); + } + + this(this) @safe + { + if (ptr !is null) + ptr = new int(*ptr); + copied = true; + } + + @disable ref S opAssign()(auto ref S rhs); + } + + { + auto foo = rebindable2(S(42)); + assert(!typeof(foo).useQualifierCast); + S.copied = false; + + auto bar = foo; + assert(S.copied); + assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr); + assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr); + } + { + auto foo = rebindable2(const S(42)); + S.copied = false; + + auto bar = foo; + assert(S.copied); + assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr); + assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr); + } + } + + // copy constructor without type qualifier cast + { + static struct S + { + int* ptr; + static bool copied; + + this(int i) + { + ptr = new int(i); + } + + this(ref inout S rhs) @safe inout + { + if (rhs.ptr !is null) + ptr = new inout int(*rhs.ptr); + copied = true; + } + + @disable ref S opAssign()(auto ref S rhs); + } + + { + auto foo = rebindable2(S(42)); + assert(!typeof(foo).useQualifierCast); + S.copied = false; + + auto bar = foo; + assert(S.copied); + assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr); + assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr); + } + { + auto foo = rebindable2(const S(42)); + S.copied = false; + + auto bar = foo; + assert(S.copied); + assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr); + assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr); + } + } + +/ +} + /** Similar to `Rebindable!(T)` but strips all qualifiers from the reference as opposed to just constness / immutability. Primary intended use case is with diff --git a/libphobos/src/std/windows/syserror.d b/libphobos/src/std/windows/syserror.d index 3d8c5e71cecc5fc977684052211433de690968d8..dadf0e8749e5b2e97e4d6a4850332be3b1576077 100644 --- a/libphobos/src/std/windows/syserror.d +++ b/libphobos/src/std/windows/syserror.d @@ -69,7 +69,6 @@ import core.sys.windows.winbase, core.sys.windows.winnt; import std.array : appender, Appender; import std.conv : to, toTextRange, text; import std.exception; -import std.windows.charset; string sysErrorString( DWORD errCode,