diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 914d54b0bb28bd481a720d937548e7907dd81cfa..679ef579a11b4646cf67aa764309e9728f9b0fd3 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -113,6 +113,7 @@ D_FRONTEND_OBJS = \ d/declaration.o \ d/delegatize.o \ d/denum.o \ + d/deps.o \ d/dimport.o \ d/dinterpret.o \ d/dmacro.o \ @@ -201,6 +202,7 @@ D_FRONTEND_OBJS = \ d/target.o \ d/templateparamsem.o \ d/templatesem.o \ + d/timetrace.o \ d/tokens.o \ d/traits.o \ d/transitivevisitor.o \ diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 6f1be64ba042756b0371d4ead37dcfc04469b968..b20f6844c14764f68ee02c05968e3d34c8e783c1 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -727,7 +727,7 @@ public: create_typeinfo (d->type, NULL); TypeEnum *tc = d->type->isTypeEnum (); - if (tc->sym->members && !d->type->isZeroInit ()) + if (tc->sym->members && !dmd::isZeroInit (d->type)) { /* Generate static initializer. */ d->sinit = enum_initializer_decl (d); @@ -821,7 +821,7 @@ public: DECL_INITIAL (decl) = build_expr (e, true); } } - else if (!d->type->isZeroInit ()) + else if (!dmd::isZeroInit (d->type)) { /* Use default initializer for the type. */ if (TypeStruct *ts = d->type->isTypeStruct ()) diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index d458bea5e1a593bfcd3455a9fe99cdc29f4c5b09..a6072c4570d5cb6a7e8b4ee2abd2940838dffc0f 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -66b93fc24a7ab5e2a8aa7f53c613df4abddc188b +34875cd6e1faa42e84ae953c0485ef524fe67e38 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 078759431515ad79f2e4b2d3a2157f62a912a9f7..00bbf84b6a18e4610bd75f5ca6c53506a78d05d1 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -43,6 +43,8 @@ Note that these groups have no strict meaning, the category assignments are a bi | [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface | | [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) | | [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions | +| [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d) | Implement the `-deps` and `-makedeps` switches | +| [timetrace.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.d) | Build time profiling utility | ### Lexing / parsing diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index ffc1c3e86dfacfcd9c22c09e361b073286358f7e..eef25e22dee41116584752779752a5ad7e209295 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.109.1 +v2.110.0-beta.1 diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d index 8d022030173300d71c4d4919b2fe33360c6d9af3..f29755bbfdf669f3cac51e3c171078e8bf370f48 100644 --- a/gcc/d/dmd/access.d +++ b/gcc/d/dmd/access.d @@ -163,7 +163,7 @@ private bool hasProtectedAccess(Scope *sc, Dsymbol s) */ bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d) { - if (sc.flags & SCOPE.noaccesscheck) + if (sc.noAccessCheck) return false; static if (LOG) { diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index e30160d72b92e04bc3c8fd37831009e993fe2b3c..5fe5273d5add6f823cdb55fce4263fd31372cdc6 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -287,9 +287,8 @@ bool isUnaArrayOp(EXP op) @safe case EXP.tilde: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -310,9 +309,8 @@ bool isBinArrayOp(EXP op) @safe case EXP.pow: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -333,9 +331,8 @@ bool isBinAssignArrayOp(EXP op) @safe case EXP.powAssign: return true; default: - break; + return false; } - return false; } /*********************************************** diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index 1c8eb57726121458e812054c8f93cfe34d8ff736..db14f9a1cc3f2111c2f00f6524ad7a52510a0b45 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -237,8 +237,7 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration * before the semantic analysis of 'to', so that template overloading based on the * 'this' pointer can be successful. */ - FuncDeclaration fd = ps.isFuncDeclaration(); - if (fd) + if (FuncDeclaration fd = ps.isFuncDeclaration()) { /* Use storage_class2 instead of storage_class otherwise when we do .di generation * we'll wind up with 'const const' rather than 'const'. @@ -984,7 +983,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope * } * - * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop + * static assert(!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop * * A StaticForeachDeclaration generates one * ForwardingAttribDeclaration for each expansion of its body. The diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index 31155f188dca62efde2b74009efa8154e21a499a..c96a65b8d40f1e4fc7b218ab0ad041b17f500782 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -111,11 +111,9 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) if (ce.f && ce.arguments.length > 0) { Type tb = (*ce.arguments)[0].type.toBasetype(); - auto tbNext = tb.nextOf(); - if (tbNext) + if (auto tbNext = tb.nextOf()) { - auto ts = tbNext.baseElemOf().isTypeStruct(); - if (ts) + if (auto ts = tbNext.baseElemOf().isTypeStruct()) { auto sd = ts.sym; const id = ce.f.ident; diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 17b33d839df82abf7d94a4c1a892752bd6dd1425..4ebe332d2240002198c86b94c392441f4b058afc 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -99,8 +99,7 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure */ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) { - Dsymbol assign = search_function(ad, Id.assign); - if (assign) + if (Dsymbol assign = search_function(ad, Id.assign)) { /* check identity opAssign exists */ @@ -825,7 +824,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) * Note that it would only be necessary if it has floating point fields. * For now, we'll just not generate a toHash() for C files. */ - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return null; //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); diff --git a/gcc/d/dmd/common/bitfields.d b/gcc/d/dmd/common/bitfields.d index 01aa56d1809e663dd12adc5df133d54ecd04596f..926cdf87eca2b8579cdabd731e9ede9b9eda6c47 100644 --- a/gcc/d/dmd/common/bitfields.d +++ b/gcc/d/dmd/common/bitfields.d @@ -20,43 +20,79 @@ module dmd.common.bitfields; extern (D) string generateBitFields(S, T)() if (__traits(isUnsigned, T)) { + import core.bitop: bsr; + string result = "extern (C++) pure nothrow @nogc @safe final {"; - enum structName = __traits(identifier, S); - string initialValue = ""; + struct BitInfo + { + int[] offset; + int[] size; + T initialValue; + int totalSize; + } + + // Iterate over members to compute bit offset and bit size for each of them + enum BitInfo bitInfo = () { + BitInfo result; + int bitOffset = 0; + foreach (size_t i, mem; __traits(allMembers, S)) + { + alias memType = typeof(__traits(getMember, S, mem)); + enum int bitSize = bsr(memType.max | 1) + 1; + result.offset ~= bitOffset; + result.size ~= bitSize; + result.initialValue |= cast(T) __traits(getMember, S.init, mem) << bitOffset; + bitOffset += bitSize; + } + result.totalSize = bitOffset; + return result; + } (); + + alias TP = typeof(T.init + 0u); // type that `T` gets promoted to, uint or ulong + enum string toString(TP i) = i.stringof; // compile time 'integer to string' + + static assert(bitInfo.totalSize <= T.sizeof * 8, + "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`"); + foreach (size_t i, mem; __traits(allMembers, S)) { - static assert(is(typeof(__traits(getMember, S, mem)) == bool)); - static assert(i < T.sizeof * 8, "too many fields for bit field storage of type `"~T.stringof~"`"); - enum mask = "(1 << "~i.stringof~")"; + enum typeName = typeof(__traits(getMember, S, mem)).stringof; + enum shift = toString!(bitInfo.offset[i]); + enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc. result ~= " - /// set or get the corresponding "~structName~" member - bool "~mem~"() const scope { return !!(bitFields & "~mask~"); } - /// ditto - bool "~mem~"(bool v) + "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); } + "~typeName~" "~mem~"("~typeName~" v) scope { - v ? (bitFields |= "~mask~") : (bitFields &= ~"~mask~"); + bitFields &= ~("~sizeMask~" << "~shift~"); + bitFields |= v << "~shift~"; return v; }"; - - initialValue = (__traits(getMember, S.init, mem) ? "1" : "0") ~ initialValue; } - return result ~ "}\n private "~T.stringof~" bitFields = 0b" ~ initialValue ~ ";\n"; + enum TP initVal = bitInfo.initialValue; + return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n"; } /// unittest { + enum E + { + a, b, c, + } + static struct B { bool x; bool y; + E e = E.c; bool z = 1; + private ubyte w = 77; } static struct S { - mixin(generateBitFields!(B, ubyte)); + mixin(generateBitFields!(B, ushort)); } S s; @@ -69,5 +105,13 @@ unittest s.y = true; assert(s.y); assert(!s.x); + + assert(s.e == E.c); + s.e = E.a; + assert(s.e == E.a); + assert(s.z); + assert(s.w == 77); + s.w = 3; + assert(s.w == 3); } diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d index 0609778e4c774428d880285dbe812adf53ea46ba..2991545faacb3102e298465804d4d32c907561a5 100644 --- a/gcc/d/dmd/cppmangle.d +++ b/gcc/d/dmd/cppmangle.d @@ -725,8 +725,7 @@ private final class CppMangleVisitor : Visitor */ static Dsymbol getInstance(Dsymbol s) { - Dsymbol p = s.toParent(); - if (p) + if (Dsymbol p = s.toParent()) { if (TemplateInstance ti = p.isTemplateInstance()) return ti; @@ -827,8 +826,7 @@ private final class CppMangleVisitor : Visitor return buf.writestring("St"); auto si = getInstance(s); - Dsymbol p = getQualifier(si); - if (p) + if (Dsymbol p = getQualifier(si)) { if (isStd(p)) { diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d index 0b3096d1f5c7fbf9f30ef8067ed018ea600dce20..09e364d7cbf2776cd4643caa8a3b91c80c2a7930 100644 --- a/gcc/d/dmd/cxxfrontend.d +++ b/gcc/d/dmd/cxxfrontend.d @@ -495,6 +495,12 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool return dmd.typesem.covariant(src, t, pstc, cppCovariant); } +bool isZeroInit(Type t, const ref Loc loc) +{ + import dmd.typesem; + return dmd.typesem.isZeroInit(t, loc); +} + bool isBaseOf(Type tthis, Type t, int* poffset) { import dmd.typesem; @@ -651,6 +657,12 @@ MATCH implicitConvTo(Type from, Type to) return dmd.dcast.implicitConvTo(from, to); } +MATCH constConv(Type from, Type to) +{ + import dmd.typesem; + return dmd.typesem.constConv(from, to); +} + /*********************************************************** * typinf.d */ diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 29059678fc2512ac6db401a77c9a94d47f687910..cda50cd078a7f821076d4432d3e6a2fb067f660b 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -71,7 +71,7 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) Expression visit(Expression e) { //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); - if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) + if (const match = (sc && sc.inCfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { // no need for an extra cast when matching is exact @@ -2140,8 +2140,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (hasAliasThis) { - auto result = tryAliasThisCast(); - if (result) + if (auto result = tryAliasThisCast()) return result; } @@ -2235,8 +2234,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) */ if (hasAliasThis) { - auto result = tryAliasThisCast(); - if (result) + if (auto result = tryAliasThisCast()) return result; } error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars()); @@ -2306,7 +2304,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed); if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid && - (!sc || !(sc.flags & SCOPE.Cfile))) + (!sc || !sc.inCfile)) { error(e.loc, "cannot convert string literal to `void*`"); return ErrorExp.get(); @@ -2437,9 +2435,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (e.committed) goto Lcast; - auto X(T, U)(T tf, U tt) + static auto X(T, U)(T tf, U tt) { - return (cast(int)tf * 256 + cast(int)tt); + return cast(int)tf * 256 + cast(int)tt; } { @@ -2557,7 +2555,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) // See if need to truncate or extend the literal if (auto tsa = tb.isTypeSArray()) { - size_t dim2 = cast(size_t)tsa.dim.toInteger(); + const dim2 = cast(size_t)tsa.dim.toInteger(); //printf("dim from = %d, to = %d\n", cast(int)se.len, cast(int)dim2); // Changing dimensions @@ -2637,8 +2635,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) tb.ty == Tpointer && tb.nextOf().ty == Tfunction) { auto ve = e.e1.isVarExp(); - auto f = ve.var.isFuncDeclaration(); - if (f) + if (auto f = ve.var.isFuncDeclaration()) { assert(f.isImportedSymbol()); f = f.overloadExactMatch(tb.nextOf()); @@ -2946,8 +2943,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (e.func) { - auto f = e.func.overloadExactMatch(tb.nextOf()); - if (f) + if (auto f = e.func.overloadExactMatch(tb.nextOf())) { int offset; if (f.tintro && f.tintro.nextOf().isBaseOf(f.type.nextOf(), &offset) && offset) @@ -3360,7 +3356,7 @@ Type typeMerge(Scope* sc, EXP op, ref Expression pe1, ref Expression pe2) Type t1b = e1.type.toBasetype(); Type t2b = e2.type.toBasetype(); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { // Integral types can be implicitly converted to pointers if ((t1b.ty == Tpointer) != (t2b.ty == Tpointer)) @@ -4220,7 +4216,7 @@ Expression integralPromotions(Expression e, Scope* sc) void fix16997(Scope* sc, UnaExp ue) { - if (global.params.fix16997 || sc.flags & SCOPE.Cfile) + if (global.params.fix16997 || sc.inCfile) ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor else { diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index 2199d5d1a7777582bd57baa24cfba5e3ea23c97a..74074d7078cda125859491ee5713ee4cdbe1ba27 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -490,8 +490,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration return null; if (cdb.ident.equals(ident)) return cdb; - auto result = cdb.searchBase(ident); - if (result) + if (auto result = cdb.searchBase(ident)) return result; } return null; diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index a1202ed628a736732b2a37d10f47a47f3a00ff89..d35857b22a7044cd5615d64553b9487eb9fabf9f 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -353,8 +353,7 @@ extern (C++) abstract class Declaration : Dsymbol // is an overload in the overload set that isn't if (isAliasedDeclaration) { - FuncDeclaration fd = isFuncDeclaration(); - if (fd) + if (FuncDeclaration fd = isFuncDeclaration()) { for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) if (!(ovl.storage_class & STC.disable)) @@ -397,7 +396,7 @@ extern (C++) abstract class Declaration : Dsymbol { for (Scope* scx = sc; scx; scx = scx.enclosing) { - if (scx.func == parent && (scx.flags & SCOPE.contract)) + if (scx.func == parent && scx.contract != Contract.none) { const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result"; if (!(flag & ModifyFlags.noError)) @@ -412,7 +411,7 @@ extern (C++) abstract class Declaration : Dsymbol VarDeclaration vthis = e1.isThisExp().var; for (Scope* scx = sc; scx; scx = scx.enclosing) { - if (scx.func == vthis.parent && (scx.flags & SCOPE.contract)) + if (scx.func == vthis.parent && scx.contract != Contract.none) { if (!(flag & ModifyFlags.noError)) error(loc, "%s `%s` cannot modify parameter `this` in contract", kind, toPrettyChars); @@ -1137,7 +1136,6 @@ extern (C++) class VarDeclaration : Declaration VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range - VarDeclarations* maybes; // maybeScope variables that are assigned to this maybeScope variable uint endlinnum; // line number of end of scope that this var lives in uint offset; @@ -1239,8 +1237,7 @@ extern (C++) class VarDeclaration : Declaration */ for (auto s = cast(Dsymbol)this; s; s = s.parent) { - auto ad = (cast(inout)s).isMember(); - if (ad) + if (auto ad = (cast(inout)s).isMember()) return ad; if (!s.parent || !s.parent.isTemplateMixin()) break; @@ -1580,7 +1577,7 @@ extern (C++) class VarDeclaration : Declaration extern (D) final bool checkNestedReference(Scope* sc, Loc loc) { //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); - if (sc.intypeof == 1 || (sc.flags & SCOPE.ctfe)) + if (sc.intypeof == 1 || sc.ctfe) return false; if (!parent || parent == sc.parent) return false; @@ -1615,7 +1612,7 @@ extern (C++) class VarDeclaration : Declaration } // Add this VarDeclaration to fdv.closureVars[] if not already there - if (!sc.intypeof && !(sc.flags & SCOPE.compile) && + if (!sc.intypeof && !sc.traitsCompiles && // https://issues.dlang.org/show_bug.cgi?id=17605 (fdv.skipCodegen || !fdthis.skipCodegen)) { diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 8f706fba33277c5391421f93741d03ff36f5fb44..4ac22b935887fff5b14b1bf8b4115659c7a75205 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -237,7 +237,6 @@ public: VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection Expression *edtor; // if !=NULL, does the destruction of the variable IntRange *range; // if !NULL, the variable is known to be within the range - VarDeclarations *maybes; // STCmaybescope variables that are assigned to this STCmaybescope variable unsigned endlinnum; // line number of end of scope that this var lives in unsigned offset; @@ -639,12 +638,10 @@ public: bool nothrowInprocess(bool v); bool nogcInprocess() const; bool nogcInprocess(bool v); - bool returnInprocess() const; - bool returnInprocess(bool v); + bool scopeInprocess() const; + bool scopeInprocess(bool v); bool inlineScanned() const; bool inlineScanned(bool v); - bool inferScope() const; - bool inferScope(bool v); bool hasCatches() const; bool hasCatches(bool v); bool skipCodegen() const; diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d index 62800d3fdb77541374ac659dff473b5227453bd3..95064b0d16dc386488c5da5e750e027de92a70ec 100644 --- a/gcc/d/dmd/delegatize.d +++ b/gcc/d/dmd/delegatize.d @@ -215,15 +215,13 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc) override void visit(SymOffExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.var.isVarDeclaration()) result = v.checkNestedReference(sc, Loc.initial); } override void visit(VarExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.var.isVarDeclaration()) result = v.checkNestedReference(sc, Loc.initial); } @@ -235,8 +233,7 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc) override void visit(DeclarationExp e) { - VarDeclaration v = e.declaration.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.declaration.isVarDeclaration()) { result = v.checkNestedReference(sc, Loc.initial); if (result) diff --git a/gcc/d/dmd/deps.d b/gcc/d/dmd/deps.d new file mode 100644 index 0000000000000000000000000000000000000000..efb4b0d4319ef0b6fa46a5885ef115e4bdb1c65c --- /dev/null +++ b/gcc/d/dmd/deps.d @@ -0,0 +1,141 @@ +/** + * Implement the `-deps` and `-makedeps` switches, which output dependencies of modules for build tools. + * + * The grammar of the `-deps` output is: + * --- + * ImportDeclaration + * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " + * ModuleAliasIdentifier ] "\n" + * + * BasicImportDeclaration + * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" + * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" + * + * FilePath + * - any string with '(', ')' and '\' escaped with the '\' character + * --- + * + * Make dependencies as generated by `-makedeps` look like this: + * --- + * source/app.d: + * source/importa.d \ + * source/importb.d + * --- + * + * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/deps.d, makedeps.d) + * Documentation: https://dlang.org/phobos/dmd_deps.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/deps.d + */ +module dmd.deps; + +import core.stdc.stdio : printf; +import core.stdc.string : strcmp; +import dmd.common.outbuffer; +import dmd.dimport : Import; +import dmd.dmodule : Module; +import dmd.globals : Param, Output; +import dmd.hdrgen : visibilityToBuffer; +import dmd.id : Id; +import dmd.utils : escapePath; + +/** + * Add an import expression to module dependencies + * Params: + * moduleDeps = output settings for `-deps` + * makeDeps = output settings for `-makedeps` + * fileNameZ = 0-termminated string containing the import expression's resolved filename + * importString = raw string passed to import exp + * imod = module import exp is in + */ +void addImportExpDep(ref Output moduleDeps, ref Output makeDeps, const(char)[] fileNameZ, const(char)[] importString, Module imod) +{ + if (moduleDeps.buffer !is null) + { + OutBuffer* ob = moduleDeps.buffer; + + if (!moduleDeps.name) + ob.writestring("depsFile "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + if (moduleDeps.name) + ob.writestring("string : "); + ob.write(importString); + ob.writestring(" ("); + escapePath(ob, fileNameZ.ptr); + ob.writestring(")"); + ob.writenl(); + } + if (makeDeps.doOutput) + { + makeDeps.files.push(fileNameZ.ptr); + } +} + +/** + * Add an import statement to module dependencies + * Params: + * moduleDeps = output settings + * imp = import to add + * imod = module that the import is in + */ +void addImportDep(ref Output moduleDeps, Import imp, Module imod) +{ + // object self-imports itself, so skip that + // https://issues.dlang.org/show_bug.cgi?id=7547 + // don't list pseudo modules __entrypoint.d, __main.d + // https://issues.dlang.org/show_bug.cgi?id=11117 + // https://issues.dlang.org/show_bug.cgi?id=11164 + if (moduleDeps.buffer is null || (imp.id == Id.object && imod.ident == Id.object) || + strcmp(imod.ident.toChars(), "__main") == 0) + return; + + OutBuffer* ob = moduleDeps.buffer; + if (!moduleDeps.name) + ob.writestring("depsImport "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + // use visibility instead of sc.visibility because it couldn't be + // resolved yet, see the comment above + visibilityToBuffer(*ob, imp.visibility); + ob.writeByte(' '); + if (imp.isstatic) + { + ob.writestring("static "); + } + ob.writestring(": "); + foreach (pid; imp.packages) + { + ob.printf("%s.", pid.toChars()); + } + ob.writestring(imp.id.toString()); + ob.writestring(" ("); + if (imp.mod) + escapePath(ob, imp.mod.srcfile.toChars()); + else + ob.writestring("???"); + ob.writeByte(')'); + foreach (i, name; imp.names) + { + if (i == 0) + ob.writeByte(':'); + else + ob.writeByte(','); + auto _alias = imp.aliases[i]; + if (!_alias) + { + ob.printf("%s", name.toChars()); + _alias = name; + } + else + ob.printf("%s=%s", _alias.toChars(), name.toChars()); + } + if (imp.aliasId) + ob.printf(" -> %s", imp.aliasId.toChars()); + ob.writenl(); +} diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 52520be01df6059aea6c8eb17cf8d2adbb4f64af..0f900c934e3949383f26a4357546afeab75c827f 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -100,6 +100,10 @@ public Expression ctfeInterpret(Expression e) auto rgnpos = ctfeGlobals.region.savePos(); + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.ctfe); + scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfe, e); + Expression result = interpret(e, null); // Report an error if the expression contained a `ThrowException` and @@ -432,6 +436,27 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars()); } + scope dlg = () { + import dmd.common.outbuffer; + auto strbuf = OutBuffer(20); + strbuf.writestring(fd.toPrettyChars()); + strbuf.write("("); + if (arguments) + { + foreach (i, arg; *arguments) + { + if (i > 0) + strbuf.write(", "); + strbuf.writestring(arg.toChars()); + } + } + strbuf.write(")"); + return strbuf.extractSlice(); + }; + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.ctfeCall); + scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfeCall, fd, dlg); + void fdError(const(char)* msg) { error(fd.loc, "%s `%s` %s", fd.kind, fd.toPrettyChars, msg); diff --git a/gcc/d/dmd/dmacro.d b/gcc/d/dmd/dmacro.d index c04fbec731d350538837b1a868573ad42b7ae742..9d702502ad1fee3201d66ed04939af514bba4036 100644 --- a/gcc/d/dmd/dmacro.d +++ b/gcc/d/dmd/dmacro.d @@ -16,8 +16,6 @@ import core.stdc.string; import dmd.common.outbuffer; import dmd.root.rmem; -@trusted: - struct MacroTable { /********************************** @@ -303,7 +301,7 @@ struct Macro * copy allocated with mem.xmalloc() */ -char[] memdup(const(char)[] p) nothrow pure +char[] memdup(const(char)[] p) nothrow pure @trusted { size_t len = p.length; return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len]; @@ -318,7 +316,7 @@ char[] memdup(const(char)[] p) nothrow pure * 1..9: get nth argument * -1: get 2nd through end */ -size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure +size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure @safe { /* Scan forward for matching right parenthesis. * Nest parentheses. @@ -334,7 +332,7 @@ size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothr uint inexp = 0; uint argn = 0; size_t v = 0; - const p = buf.ptr; + const p = buf; const end = buf.length; Largstart: // Skip first space, if any, to find the start of the macro argument diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 005f1c9d0e29548cfdf4788b8c641515966711a3..583b4679f0b393b6b0a6116a6972648dda643dc6 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -715,6 +715,11 @@ extern (C++) final class Module : Package { const(char)* srcname = srcfile.toChars(); //printf("Module::parse(srcname = '%s')\n", srcname); + + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.parse); + scope (exit) timeTraceEndEvent(TimeTraceEventType.parse, this); + isPackageFile = isPackageFileName(srcfile); const(char)[] buf = processSource(src, this); // an error happened on UTF conversion diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index 4719529554780cbbd4f37a3d2d5b75f4480bb34a..7936086bdd06d392943a1a1d6403a7b8ae8af293 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -45,37 +45,45 @@ import dmd.tokens; //version=LOGSEARCH; - -// List of flags that can be applied to this `Scope` -enum SCOPE +/// What kind of contract function we're in, if any +enum Contract : ubyte { - ctor = 0x0001, /// constructor type - noaccesscheck = 0x0002, /// don't do access checks - condition = 0x0004, /// inside static if/assert condition - debug_ = 0x0008, /// inside debug conditional - constraint = 0x0010, /// inside template constraint - invariant_ = 0x0020, /// inside invariant code - require = 0x0040, /// inside in contract code - ensure = 0x0060, /// inside out contract code - contract = 0x0060, /// [mask] we're inside contract code - ctfe = 0x0080, /// inside a ctfe-only expression - compile = 0x0100, /// inside __traits(compile) - ignoresymbolvisibility = 0x0200, /// ignore symbol visibility - /// https://issues.dlang.org/show_bug.cgi?id=15907 - Cfile = 0x0800, /// C semantics apply - free = 0x8000, /// is on free list - - fullinst = 0x10000, /// fully instantiate templates - ctfeBlock = 0x20000, /// inside a `if (__ctfe)` block - dip1000 = 0x40000, /// dip1000 errors enabled for this scope - dip25 = 0x80000, /// dip25 errors enabled for this scope + none = 0, + invariant_ = 1, + require = 2, // in contract + ensure = 3, // out contract } -/// Flags that are carried along with a scope push() -private enum PersistentFlags = - SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint | - SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility | - SCOPE.Cfile | SCOPE.ctfeBlock | SCOPE.dip1000 | SCOPE.dip25; +private extern (D) struct BitFields +{ + bool ctor; /// constructor type + bool noAccessCheck; /// don't do access checks + bool condition; /// inside static if/assert condition + bool debug_; /// inside debug conditional + bool inTemplateConstraint; /// inside template constraint + 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 + bool dip25; /// dip25 errors enabled for this scope +} extern (C++) struct Scope { @@ -136,7 +144,8 @@ extern (C++) struct Scope DeprecatedDeclaration depdecl; /// customized deprecation message - uint flags; + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, uint)); // user defined attributes UserAttributeDeclaration userAttribDecl; @@ -157,8 +166,8 @@ extern (C++) struct Scope Scope* s = freelist; freelist = s.enclosing; //printf("freelist %p\n", s); - assert(s.flags & SCOPE.free); - s.flags &= ~SCOPE.free; + assert(s.canFree); + s.canFree = false; return s; } return new Scope(); @@ -181,11 +190,11 @@ extern (C++) struct Scope m.addMember(null, sc.scopesym); m.parent = null; // got changed by addMember() if (global.params.useDIP1000 == FeatureState.enabled) - sc.flags |= SCOPE.dip1000; + sc.dip1000 = true; if (global.params.useDIP25 == FeatureState.enabled) - sc.flags |= SCOPE.dip25; + sc.dip25 = true; if (_module.filetype == FileType.c) - sc.flags |= SCOPE.Cfile; + sc.inCfile = true; // Create the module scope underneath the global scope sc = sc.push(_module); sc.parent = _module; @@ -207,13 +216,13 @@ extern (C++) struct Scope { Scope* s = copy(); //printf("Scope::push(this = %p) new = %p\n", this, s); - assert(!(flags & SCOPE.free)); + assert(!this.canFree); s.scopesym = null; s.enclosing = &this; debug { if (enclosing) - assert(!(enclosing.flags & SCOPE.free)); + assert(!enclosing.canFree); if (s == enclosing) { printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing); @@ -223,12 +232,38 @@ extern (C++) struct Scope s.slabel = null; s.nofree = false; s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; - s.flags = (flags & PersistentFlags); + + // Only keep persistent flags + s.resetAllFlags(); + s.contract = this.contract; + s.debug_ = this.debug_; + s.ctfe = this.ctfe; + s.traitsCompiles = this.traitsCompiles; + s.inTemplateConstraint = this.inTemplateConstraint; + s.noAccessCheck = this.noAccessCheck; + s.ignoresymbolvisibility = this.ignoresymbolvisibility; + s.inCfile = this.inCfile; + s.ctfeBlock = this.ctfeBlock; + s.dip1000 = this.dip1000; + s.dip25 = this.dip25; + s.lastdc = null; assert(&this != s); return s; } + /// Copy flags from scope `other` + extern(D) void copyFlagsFrom(Scope* other) + { + this.bitFields = other.bitFields; + } + + /// Set all scope flags to their initial value + extern(D) void resetAllFlags() + { + this.bitFields = 0; + } + extern (D) Scope* push(ScopeDsymbol ss) { //printf("Scope::push(%s)\n", ss.toChars()); @@ -251,7 +286,7 @@ extern (C++) struct Scope this = this.init; enclosing = freelist; freelist = &this; - flags |= SCOPE.free; + this.canFree = true; } return enc; } @@ -270,7 +305,8 @@ extern (C++) struct Scope extern (D) Scope* startCTFE() { Scope* sc = this.push(); - sc.flags = this.flags | SCOPE.ctfe; + sc.copyFlagsFrom(&this); + sc.ctfe = true; version (none) { /* TODO: Currently this is not possible, because we need to @@ -300,7 +336,7 @@ extern (C++) struct Scope extern (D) Scope* endCTFE() { - assert(flags & SCOPE.ctfe); + assert(this.ctfe); return pop(); } @@ -470,7 +506,7 @@ extern (C++) struct Scope if (sc.scopesym.isModule()) flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed - else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration()) + else if (sc.inCfile && sc.scopesym.isStructDeclaration()) continue; // C doesn't have struct scope if (Dsymbol s = sc.scopesym.search(loc, ident, flags)) @@ -495,8 +531,7 @@ extern (C++) struct Scope if (global.params.fixAliasThis) { Expression exp = new ThisExp(loc); - Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp); - if (aliasSym) + if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp)) { //printf("found aliassym: %s\n", aliasSym.toChars()); pscopesym = new ExpressionDsymbol(exp); @@ -511,7 +546,7 @@ extern (C++) struct Scope return null; } - if (this.flags & SCOPE.ignoresymbolvisibility) + if (this.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; // First look in local scopes @@ -658,7 +693,7 @@ extern (C++) struct Scope //printf("\t\tscopesym = %p\n", scopesym); if (!scopesym.symtab) scopesym.symtab = new DsymbolTable(); - if (!(flags & SCOPE.Cfile)) + if (!this.inCfile) return scopesym.symtabInsert(s); // ImportC insert @@ -753,7 +788,7 @@ extern (C++) struct Scope { //printf("\tsc = %p\n", sc); sc.nofree = true; - assert(!(flags & SCOPE.free)); + assert(!this.canFree); //assert(sc != sc.enclosing); //assert(!sc.enclosing || sc != sc.enclosing.enclosing); //if (++i == 10) @@ -814,7 +849,7 @@ extern (C++) struct Scope */ extern (D) bool isFromSpeculativeSemanticContext() scope { - return this.intypeof || this.flags & SCOPE.compile; + return this.intypeof || this.traitsCompiles; } @@ -824,19 +859,19 @@ extern (C++) struct Scope */ extern (D) bool needsCodegen() { - return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0; + return !this.ctfe && !this.ctfeBlock && !this.traitsCompiles; } /// Returns: whether to raise DIP1000 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP1000() { - return (flags & SCOPE.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; + return (this.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; } /// Returns: whether to raise DIP25 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP25() { - return (flags & SCOPE.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; + return (this.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; } /// Returns: whether this scope compiles with `edition` or later diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index 90b2d9fda6b58418676ee06d94af2c8a9fb095ff..f5ec1ce1251c9afa60c71a6c76ecf64c26204c68 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -265,10 +265,9 @@ extern (C++) class Dsymbol : ASTNode Identifier ident; Dsymbol parent; Symbol* csym; // symbol for code generator - const Loc loc; // where defined Scope* _scope; // !=null means context to use for semantic() - const(char)* prettystring; // cached value of toPrettyChars() private DsymbolAttributes* atts; /// attached attribute declarations + const Loc loc; // where defined bool errors; // this symbol failed to pass semantic() PASS semanticRun = PASS.initial; ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab @@ -396,8 +395,7 @@ extern (C++) class Dsymbol : ASTNode while (s) { //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); - Module m = s.isModule(); - if (m) + if (Module m = s.isModule()) return m; s = s.parent; } @@ -428,8 +426,7 @@ extern (C++) class Dsymbol : ASTNode while (s) { //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); - Module m = s.isModule(); - if (m) + if (Module m = s.isModule()) return m; TemplateInstance ti = s.isTemplateInstance(); if (ti && ti.enclosing) @@ -657,15 +654,10 @@ extern (C++) class Dsymbol : ASTNode const(char)* toPrettyChars(bool QualifyTypes = false) { - if (prettystring && !QualifyTypes) - return prettystring; // value cached for speed - //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); if (!parent) { auto s = toChars(); - if (!QualifyTypes) - prettystring = s; return s; } @@ -685,8 +677,6 @@ extern (C++) class Dsymbol : ASTNode addQualifiers(this); auto s = buf.extractSlice(true).ptr; - if (!QualifyTypes) - prettystring = s; return s; } diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index ce29073e2ea8527176ad08c132938a445331028f..2c5f0e579b3af7974373056a7a2cdf1c4e0b5e97 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -192,12 +192,11 @@ public: Identifier *ident; Dsymbol *parent; Symbol *csym; // symbol for code generator - Loc loc; // where defined Scope *_scope; // !=NULL means context to use for semantic() - const utf8_t *prettystring; private: DsymbolAttributes* atts; public: + Loc loc; // where defined d_bool errors; // this symbol failed to pass semantic() PASS semanticRun; unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index e32f5fa221c72ba1f8010815e6cba0d098923fea..711ab122609b48c69f7ba2e65efe21072225c88e 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -24,10 +24,12 @@ import dmd.attrib; import dmd.attribsem; import dmd.clone; import dmd.cond; +import dmd.timetrace; import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.denum; +import dmd.deps; import dmd.dimport; import dmd.dinterpret; import dmd.dmodule; @@ -59,6 +61,7 @@ import dmd.objc; import dmd.opover; import dmd.optimize; import dmd.parse; +debug import dmd.printast; import dmd.root.array; import dmd.root.filename; import dmd.common.outbuffer; @@ -127,7 +130,7 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc) else { auto n = e.toInteger(); - if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment + if (sc.inCfile && n == 0) // C11 6.7.5-6 allows 0 for alignment continue; if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isintegral()) @@ -179,7 +182,7 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) return false; // Don't complain if we're inside a template constraint // https://issues.dlang.org/show_bug.cgi?id=21831 - if (sc.flags & SCOPE.constraint) + if (sc.inTemplateConstraint) return false; const(char)* message = null; @@ -596,7 +599,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } if (dsym.storage_class & STC.extern_ && dsym._init) - .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars); + { + if (sc.inCfile) + { + // https://issues.dlang.org/show_bug.cgi?id=24447 + // extern int x = 3; is allowed in C + dsym.storage_class &= ~STC.extern_; + } + else + .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars); + + } AggregateDeclaration ad = dsym.isThis(); if (ad) @@ -614,12 +627,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func; if (needctfe) { - sc.flags |= SCOPE.condition; + sc.condition = true; sc = sc.startCTFE(); } //printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars()); dsym._init = dsym._init.inferType(sc); - dsym.type = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0).type; + dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type; if (needctfe) sc = sc.endCTFE(); @@ -728,7 +741,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor * and add those. */ size_t nelems = Parameter.dim(tt.arguments); - Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0) : null; + Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null; if (ie) ie = ie.expressionSemantic(sc); if (nelems > 0 && ie) @@ -1001,9 +1014,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This) + if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This) { - .error(dsym.loc, "%s `%s` - only parameters, functions and `foreach` declarations can be `ref`", dsym.kind, dsym.toPrettyChars); + .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars); } if (dsym.type.hasWild()) @@ -1052,8 +1065,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } + bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_; + if (dsymIsRef) + { + if (!dsym._init && dsym.ident != Id.This) + .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars); + } + FuncDeclaration fd = parent.isFuncDeclaration(); - if (dsym.type.isscope() && !(dsym.storage_class & STC.nodtor)) + if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor)) { if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd) { @@ -1122,7 +1142,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool isBlit = false; uinteger_t sz; - if (sc.flags & SCOPE.Cfile && !dsym._init) + if (sc.inCfile && !dsym._init) { addDefaultCInitializer(dsym); } @@ -1187,7 +1207,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable); - if (sc.flags & SCOPE.Cfile && + if (sc.inCfile && dsym.type.isTypeSArray() && dsym.type.isTypeSArray().isIncomplete() && dsym._init.isVoidInitializer() && @@ -1217,12 +1237,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (ai && tb.ty == Taarray) e = ai.toAssocArrayLiteral(); else - e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + e = dsym._init.initializerToExpression(null, sc.inCfile); if (!e) { // Run semantic, but don't need to interpret dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret); - e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + e = dsym._init.initializerToExpression(null, sc.inCfile); if (!e) { .error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars); @@ -1232,7 +1252,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ei = new ExpInitializer(dsym._init.loc, e); dsym._init = ei; } - else if (sc.flags & SCOPE.Cfile && dsym.type.isTypeSArray() && + else if (sc.inCfile && dsym.type.isTypeSArray() && dsym.type.isTypeSArray().isIncomplete()) { // C11 6.7.9-22 determine the size of the incomplete array, @@ -1283,21 +1303,48 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Expression exp = ei.exp; Expression e1 = new VarExp(dsym.loc, dsym); - if (isBlit) - exp = new BlitExp(dsym.loc, e1, exp); + if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach() + { + dsym.storage_class |= STC.nodtor; + exp = exp.expressionSemantic(sc); + Type tp = dsym.type; + Type ta = exp.type; + if (!exp.isLvalue()) + { + .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars()); + exp = ErrorExp.get(); + } + else if (!ta.constConv(tp)) + { + .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars()); + exp = ErrorExp.get(); + } + else + { + exp = new ConstructExp(dsym.loc, e1, exp); + dsym.canassign++; + exp = exp.expressionSemantic(sc); + dsym.canassign--; + } + } else - exp = new ConstructExp(dsym.loc, e1, exp); - dsym.canassign++; - exp = exp.expressionSemantic(sc); - dsym.canassign--; - exp = exp.optimize(WANTvalue); + { + if (isBlit) + exp = new BlitExp(dsym.loc, e1, exp); + else + exp = new ConstructExp(dsym.loc, e1, exp); + dsym.canassign++; + exp = exp.expressionSemantic(sc); + dsym.canassign--; + } + if (exp.op == EXP.error) { dsym._init = new ErrorInitializer(); ei = null; } else - ei.exp = exp; + ei.exp = exp.optimize(WANTvalue); } else { @@ -1321,7 +1368,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) || dsym.type.isConst() || dsym.type.isImmutable() || - sc.flags & SCOPE.Cfile) + sc.inCfile) { /* Because we may need the results of a const declaration in a * subsequent type, such as an array dimension, before semantic2() @@ -1466,7 +1513,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dsym.errors) return; - if (!(global.params.bitfields || sc.flags & SCOPE.Cfile)) + if (!(global.params.bitfields || sc.inCfile)) { version (IN_GCC) .error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars); @@ -1514,6 +1561,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(Import imp) { + timeTraceBeginEvent(TimeTraceEventType.sema1Import); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp); static if (LOG) { printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars()); @@ -1635,75 +1684,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } imp.semanticRun = PASS.semanticdone; - - // object self-imports itself, so skip that - // https://issues.dlang.org/show_bug.cgi?id=7547 - // don't list pseudo modules __entrypoint.d, __main.d - // https://issues.dlang.org/show_bug.cgi?id=11117 - // https://issues.dlang.org/show_bug.cgi?id=11164 - if (global.params.moduleDeps.buffer is null || (imp.id == Id.object && sc._module.ident == Id.object) || - strcmp(sc._module.ident.toChars(), "__main") == 0) - return; - - /* The grammar of the file is: - * ImportDeclaration - * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " - * ModuleAliasIdentifier ] "\n" - * - * BasicImportDeclaration - * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" - * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" - * - * FilePath - * - any string with '(', ')' and '\' escaped with the '\' character - */ - OutBuffer* ob = global.params.moduleDeps.buffer; - Module imod = sc._module; - if (!global.params.moduleDeps.name) - ob.writestring("depsImport "); - ob.writestring(imod.toPrettyChars()); - ob.writestring(" ("); - escapePath(ob, imod.srcfile.toChars()); - ob.writestring(") : "); - // use visibility instead of sc.visibility because it couldn't be - // resolved yet, see the comment above - visibilityToBuffer(*ob, imp.visibility); - ob.writeByte(' '); - if (imp.isstatic) - { - stcToBuffer(*ob, STC.static_); - ob.writeByte(' '); - } - ob.writestring(": "); - foreach (pid; imp.packages) - { - ob.printf("%s.", pid.toChars()); - } - ob.writestring(imp.id.toString()); - ob.writestring(" ("); - if (imp.mod) - escapePath(ob, imp.mod.srcfile.toChars()); - else - ob.writestring("???"); - ob.writeByte(')'); - foreach (i, name; imp.names) - { - if (i == 0) - ob.writeByte(':'); - else - ob.writeByte(','); - Identifier _alias = imp.aliases[i]; - if (!_alias) - { - ob.printf("%s", name.toChars()); - _alias = name; - } - else - ob.printf("%s=%s", _alias.toChars(), name.toChars()); - } - if (imp.aliasId) - ob.printf(" -> %s", imp.aliasId.toChars()); - ob.writenl(); + addImportDep(global.params.moduleDeps, imp, sc._module); } void attribSemantic(AttribDeclaration ad) @@ -1754,7 +1735,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared); sc.inunion = scd.isunion ? scd : null; - sc.flags = 0; + sc.resetAllFlags(); for (size_t i = 0; i < scd.decl.length; i++) { Dsymbol s = (*scd.decl)[i]; @@ -1911,6 +1892,25 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { if (sa.semanticRun < PASS.semanticdone) sa.semanticRun = PASS.semanticdone; + else + return; + + // https://issues.dlang.org/show_bug.cgi?id=24645 + // This is a short-circuit. Usually, static assert conditions are evaluated + // in semantic2, but it's not uncommon to use this pattern: + // --- + // version(X) + // {} + // else + // static assert(false, "unsupported platform"); + // --- + // However, without this short-circuit, the static assert error may get drowned + // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though, + // inside mixin templates you want an instantiation trace (which you don't get here). + if (sc.parent && sc.parent.isModule()) + if (auto i = sa.exp.isIntegerExp()) + if (i.toInteger() == 0) + staticAssertFail(sa, sc); } override void visit(DebugSymbol ds) @@ -1936,6 +1936,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { if (m.semanticRun != PASS.initial) return; + + timeTraceBeginEvent(TimeTraceEventType.sema1Module); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m); + //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); m.semanticRun = PASS.semantic; // Note that modules get their own scope, from scratch. @@ -2782,7 +2786,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~STC.static_; // not a static invariant sc.stc |= STC.const_; // invariant() is always const - sc.flags = (sc.flags & ~SCOPE.contract) | SCOPE.invariant_; + sc.contract = Contract.invariant_; sc.linkage = LINK.d; funcDeclarationSemantic(sc, invd); @@ -3001,7 +3005,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buildOpAssign(sd, sc2); buildOpEquals(sd, sc2); - if (!(sc2.flags & SCOPE.Cfile) && + if (!sc2.inCfile && global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo { sd.xeq = buildXopEquals(sd, sc2); @@ -4137,7 +4141,7 @@ private extern(C++) class AddMemberVisitor : Visitor } // If using C tag/prototype/forward declaration rules - if (sc.flags & SCOPE.Cfile && !dsym.isImport()) + if (sc.inCfile && !dsym.isImport()) { if (handleTagSymbols(*sc, dsym, s2, sds)) return; @@ -4158,7 +4162,7 @@ private extern(C++) class AddMemberVisitor : Visitor if (sds.isAggregateDeclaration() || sds.isEnumDeclaration()) { if (dsym.ident == Id.__sizeof || - !(sc && sc.flags & SCOPE.Cfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof)) + !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof)) { .error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars()); dsym.errors = true; @@ -4434,7 +4438,7 @@ private extern(C++) class AddMemberVisitor : Visitor */ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) { - const bool isCEnum = (sc.flags & SCOPE.Cfile) != 0; // it's an ImportC enum + const bool isCEnum = sc.inCfile; // it's an ImportC enum //printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum); if (ed.added) return; @@ -4465,6 +4469,8 @@ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) * the enum members to both symbol tables. */ em.addMember(sc, ed); // add em to ed's symbol table + if (em.errors) + return; em.addMember(sc, sds); // add em to symbol table that ed is in em.parent = ed; // restore it after previous addMember() changed it } @@ -4970,7 +4976,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList if (global.errors != errorsave) goto Laftersemantic; - if ((sc.func || (sc.flags & SCOPE.fullinst)) && !tempinst.tinst) + if ((sc.func || sc.fullinst) && !tempinst.tinst) { /* If a template is instantiated inside function, the whole instantiation * should be done at that position. But, immediate running semantic3 of @@ -7267,9 +7273,11 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } else if (style == TargetC.BitFieldStyle.Gcc_Clang) { - // If the bit-field spans more units of alignment than its type, - // start a new field at the next alignment boundary. - if (fieldState.bitOffset == fieldState.fieldSize * 8 && + // If the bit-field spans more units of alignment than its type + // and is at the alignment boundary, start a new field at the + // next alignment boundary. This affects when offsetof reports + // a higher number and bitoffsetof starts at zero again. + if (fieldState.bitOffset % (memalignsize * 8) == 0 && fieldState.bitOffset + bfd.fieldWidth > memsize * 8) { if (log) printf("more units of alignment than its type\n"); diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 8fcbbad2f76953e5e0ee1848fa72568c0cf94bed..6883180107a31b97dcd086d594b6bfffa9cf3d1e 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -423,8 +423,7 @@ private size_t arrayObjectHash(ref Objects oa1) hash = mixHash(hash, expressionHash(e1)); else if (auto s1 = isDsymbol(o1)) { - auto fa1 = s1.isFuncAliasDeclaration(); - if (fa1) + if (auto fa1 = s1.isFuncAliasDeclaration()) s1 = fa1.toAliasFunc(); hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent)); } @@ -1012,6 +1011,11 @@ size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) return IDX_NOTFOUND; } +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + ubyte deduceWildHelper(Type t, Type* at, Type tparam) { if ((tparam.mod & MODFlags.wild) == 0) @@ -1019,11 +1023,6 @@ ubyte deduceWildHelper(Type t, Type* at, Type tparam) *at = null; - auto X(T, U)(T U, U T) - { - return (U << 4) | T; - } - switch (X(tparam.mod, t.mod)) { case X(MODFlags.wild, 0): @@ -1098,12 +1097,6 @@ private Type rawTypeMerge(Type t1, Type t2) MATCH deduceTypeHelper(Type t, out Type at, Type tparam) { // 9*9 == 81 cases - - auto X(T, U)(T U, U T) - { - return (U << 4) | T; - } - switch (X(tparam.mod, t.mod)) { case X(0, 0): @@ -2557,8 +2550,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa */ private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches) { - TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null; - if (parti) + if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null) { // Make a temporary copy of dedtypes so we don't destroy it auto tmpdedtypes = new Objects(dedtypes.length); diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d index c67ac618ec3e137a4b6eb8e830ff148a3f3a5fca..e1eadc531f132f5c3a4c7a43e7a781a8ec1c5374 100644 --- a/gcc/d/dmd/enumsem.d +++ b/gcc/d/dmd/enumsem.d @@ -192,7 +192,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) return; } - if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done + if (!sc.inCfile) // C enum remains incomplete until members are done ed.semanticRun = PASS.semanticdone; version (none) @@ -219,8 +219,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) */ ed.members.foreachDsymbol( (s) { - EnumMember em = s.isEnumMember(); - if (em) + if (EnumMember em = s.isEnumMember()) em._scope = sce; }); @@ -230,7 +229,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) */ addEnumMembersToSymtab(ed, sc, sc.getScopesym()); - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* C11 6.7.2.2 */ @@ -386,8 +385,7 @@ Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) foreach (const i; 0 .. ed.members.length) { - EnumMember em = (*ed.members)[i].isEnumMember(); - if (em) + if (EnumMember em = (*ed.members)[i].isEnumMember()) { if (em.semanticRun < PASS.semanticdone) { diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index 08bd6fd2fdd662beeef71f1cd3a32f814de833f0..fd2bd00ee485e5652cca91373d86d9f22c40595a 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -378,32 +378,24 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI void onValue(VarDeclaration v) { if (log) printf("byvalue %s\n", v.toChars()); - if (parStc & STC.scope_ || v.isDataseg()) + if (parStc & STC.scope_) return; - Dsymbol p = v.toParent2(); - - notMaybeScope(v, vPar); + doNotInferScope(v, vPar); if (v.isScope()) { unsafeAssign!"scope variable"(v); } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - unsafeAssign!"variadic variable"(v); - } } void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref %s\n", v.toChars()); - if (v.isDataseg()) - return; Dsymbol p = v.toParent2(); - notMaybeScope(v, arg); + doNotInferScope(v, arg); if (checkScopeVarAddr(v, arg, sc, gag)) { result = true; @@ -432,7 +424,7 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI Dsymbol p = v.toParent2(); - notMaybeScope(v, arg); + doNotInferScope(v, arg); if ((v.isReference() || v.isScope()) && p == sc.func) { @@ -667,6 +659,13 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) va = null; } + if (e.op == EXP.construct && va && (va.storage_class & STC.temp) && va._init) + { + // Initializing a temporary is safe, `escapeExp` will forward such vars + // to their `va._init` if needed. + return false; + } + if (log && va) printf("va: %s\n", va.toChars()); FuncDeclaration fd = sc.func; @@ -701,32 +700,19 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) void onValue(VarDeclaration v) { if (log) printf("byvalue: %s\n", v.toChars()); - if (v.isDataseg()) - return; if (v == va) return; Dsymbol p = v.toParent2(); - if (va && !vaIsRef && !va.isScope() && !v.isScope() && - !v.isTypesafeVariadicArray && !va.isTypesafeVariadicArray && - (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) && - p == fd) - { - /* Add v to va's list of dependencies - */ - va.addMaybe(v); - return; - } - if (vaIsFirstRef && p == fd) { inferReturn(fd, v, /*returnScope:*/ true); } if (!(va && va.isScope()) || vaIsRef) - notMaybeScope(v, e); + doNotInferScope(v, e); if (v.isScope()) { @@ -785,27 +771,20 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) } result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1); } - else if (v.isTypesafeVariadicArray && p == fd) - { - if (inferScope(va)) - return; - result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1); - } else { /* v is not 'scope', and we didn't check the scope of where we assigned it to. * It may escape via that assignment, therefore, v can never be 'scope'. */ //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__); - doNotInferScope(v, e); + if (!v.isParameter) + doNotInferScope(v, e); } } void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref: %s\n", v.toChars()); - if (v.isDataseg()) - return; if (checkScopeVarAddr(v, ae, sc, gag)) { @@ -845,7 +824,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) } if (!(va && va.isScope())) - notMaybeScope(v, e); + doNotInferScope(v, e); if (p != sc.func) return; @@ -856,8 +835,6 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) va.storage_class |= STC.return_ | STC.returninferred; return; } - if (e1.op == EXP.structLiteral) - return; result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1); } @@ -884,7 +861,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) Dsymbol p = v.toParent2(); if (!(va && va.isScope())) - notMaybeScope(v, e); + doNotInferScope(v, e); if (!(v.isReference() || v.isScope()) || p != fd) return; @@ -909,8 +886,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) /* Do not allow slicing of a static array returned by a function */ - if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() && - !(va && va.storage_class & STC.temp)) + if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray()) { if (!gag) sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`", @@ -919,31 +895,11 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) return; } - if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() && - (!va || !(va.storage_class & STC.temp) && !va.isScope())) - { - if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1)) - { - result = true; - return; - } - } - - if (ee.op == EXP.structLiteral && - (!va || !(va.storage_class & STC.temp))) - { - if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1)) - { - result = true; - return; - } - } - - if (inferScope(va)) - return; + const(char)* msg = (ee.op == EXP.structLiteral) ? + "address of struct literal `%s` assigned to `%s` with longer lifetime" : + "address of expression temporary returned by `%s` assigned to `%s` with longer lifetime"; - result |= sc.setUnsafeDIP1000(gag, ee.loc, - "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1); + result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, e1); } scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); @@ -977,9 +933,6 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag) void onValue(VarDeclaration v) { //printf("byvalue %s\n", v.toChars()); - if (v.isDataseg()) - return; - if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown // despite being `scope` { @@ -989,7 +942,7 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag) } else { - notMaybeScope(v, new ThrowExp(e.loc, e)); + doNotInferScope(v, new ThrowExp(e.loc, e)); } } void onFunc(FuncDeclaration fd, bool called) {} @@ -1025,8 +978,6 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); - if (v.isDataseg()) - return; Dsymbol p = v.toParent2(); @@ -1049,15 +1000,10 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) return; } } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - result |= sc.setUnsafeDIP1000(gag, e.loc, - "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e, v); - } else { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); - notMaybeScope(v, e); + doNotInferScope(v, e); } } @@ -1075,9 +1021,6 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) return setUnsafePreview(&sc, fs, gag, e.loc, msg, e, v); } - if (v.isDataseg()) - return; - Dsymbol p = v.toParent2(); if (!v.isReference()) @@ -1124,7 +1067,12 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) } } - void onFunc(FuncDeclaration fd, bool called) {} + void onFunc(FuncDeclaration fd, bool called) + { + if (called) + result |= sc.setUnsafeDIP1000(gag, e.loc, + "nested function `%s` returns `scope` values and escapes them into allocated memory", fd); + } void onExp(Expression ee, bool retRefTransition) { @@ -1203,8 +1151,6 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); - if (v.isDataseg()) - return; const vsr = buildScopeRef(v.storage_class); @@ -1215,7 +1161,13 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g return; } - if (v.isScope()) + if (v.isTypesafeVariadicArray && p == sc.func) + { + if (!gag) + sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); + result = false; + } + else if (v.isScope()) { /* If `return scope` applies to v. */ @@ -1270,13 +1222,7 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g } } } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - if (!gag) - sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); - result = false; - } - else + else if (p == sc.func || !v.isParameter()) { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); doNotInferScope(v, e); @@ -1332,24 +1278,15 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g } } - if (v.isDataseg()) - return; - const vsr = buildScopeRef(v.storage_class); Dsymbol p = v.toParent2(); // https://issues.dlang.org/show_bug.cgi?id=19965 - if (!refs) + if (!refs && checkScopeVarAddr(v, e, sc, gag)) { - if (sc.func.vthis == v) - notMaybeScope(v, e); - - if (checkScopeVarAddr(v, e, sc, gag)) - { - result = true; - return; - } + result = true; + return; } if (!v.isReference()) @@ -1360,7 +1297,7 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g return; } FuncDeclaration fd = p.isFuncDeclaration(); - if (fd && sc.func.returnInprocess) + if (fd && sc.func.scopeInprocess) { /* Code like: * int x; @@ -1488,7 +1425,7 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) if (!v.isParameter() || v.isTypesafeVariadicArray || (returnScope && v.doNotInferReturn)) return false; - if (!fd.returnInprocess) + if (!fd.scopeInprocess) return false; if (returnScope && !(v.isScope() || v.maybeScope)) @@ -1546,10 +1483,20 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) * Params: * e = expression to be returned by value * er = where to place collected data - * retRefTransition = if `e` is returned through a `return (ref) scope` function call */ public -void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransition = false) +void escapeByValue(Expression e, ref scope EscapeByResults er) +{ + escapeExp(e, er, 0); +} + +// Unified implementation of `escapeByValue` and `escapeByRef` +// deref = derference level, if `p` has deref 0, then `*p` has deref 1, `&p` has -1, and `**p` has 2 etc. +// For escapeByValue, deref = 0 +// For escapeByRef, deref = -1 +// Currently, `scope` is not transitive, so deref > 0 means no escaping, but `@live` does do transitive checking, +// and future enhancements might add some form of transitive scope. +void escapeExp(Expression e, ref scope EscapeByResults er, int deref) { //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars()); @@ -1563,55 +1510,92 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi * allowed, but CTFE can generate one out of a new expression, * but it'll be placed in static data so no need to check it. */ - if (e.e1.op != EXP.structLiteral) - escapeByRef(e.e1, er, retRefTransition); + if (deref == 0 && e.e1.op != EXP.structLiteral) + escapeExp(e.e1, er, deref - 1); } void visitSymOff(SymOffExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) - er.byRef(v, retRefTransition); + if (VarDeclaration v = e.var.isVarDeclaration()) + er.varDeref(v, deref - 1); } void visitVar(VarExp e) { if (auto v = e.var.isVarDeclaration()) { - if (v.type.hasPointers() || // not tracking non-pointers - v.storage_class & STC.lazy_) // lazy variables are actually pointers - er.byValue(v); + const refAddr = deref < 0 && v.storage_class & STC.ref_ ; + const tempVar = deref == 0 && v.storage_class & STC.temp; + if ((refAddr || tempVar) && v._init && v != er.lastTemp) + { + // If compiler generated ref temporary + // (ref v = ex; ex) + // e.g. to extract side effects of `Tuple!(int, int).modify().expand[0]` + // look at the initializer instead + if (ExpInitializer ez = v._init.isExpInitializer()) + { + // Prevent endless loops. Consider: + // `__field0 = (S __tup1 = S(x, y);) , __field0 = __tup1.__fields_field_0` + // escapeExp would recurse on the lhs of the last assignment, which is __field0 + // again. In this case, we want the rhs. + // Also consider appending a struct with a `return scope` constructor: + // __appendtmp34 = __appendtmp34.this(null) + // In that case we just break the cycle using `lastTemp`. + auto lc = ez.exp.lastComma(); + auto restoreLastTemp = er.lastTemp; + er.lastTemp = v; + // printf("%s %s TO %s\n", e.loc.toChars, e.toChars, lc.toChars); + if (lc.isAssignExp || lc.isConstructExp || lc.isBlitExp) + escapeExp(lc.isBinExp().e2, er, deref); + else + escapeExp(ez.exp, er, deref); + + er.lastTemp = restoreLastTemp; + return; + } + } + + if (deref < 0 || e.type.hasPointers()) + er.varDeref(v, deref); } } void visitThis(ThisExp e) { + // Special case because `__this2` isn't `ref` internally + if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) + { + escapeByValue(e, er); + return; + } + if (e.var) - er.byValue(e.var); + er.varDeref(e.var, deref); } void visitPtr(PtrExp e) { - if (er.live && e.type.hasPointers()) - escapeByValue(e.e1, er, retRefTransition); + if (deref < 0 || (er.live && e.type.hasPointers())) + escapeExp(e.e1, er, deref + 1); } void visitDotVar(DotVarExp e) { - auto t = e.e1.type.toBasetype(); - if (e.type.hasPointers() && (er.live || t.ty == Tstruct)) - { - escapeByValue(e.e1, er, retRefTransition); - } + auto t1b = e.e1.type.toBasetype(); + // Accessing a class field dereferences the `this` pointer + if (t1b.isTypeClass()) + escapeExp(e.e1, er, deref + 1); + else if (deref < 0 || e.type.hasPointers()) + escapeExp(e.e1, er, deref); } void visitDelegate(DelegateExp e) { Type t = e.e1.type.toBasetype(); - if (t.ty == Tclass || t.ty == Tpointer) - escapeByValue(e.e1, er, retRefTransition); + if (t.isTypeClass() || t.isTypePointer()) + escapeByValue(e.e1, er); else - escapeByRef(e.e1, er, retRefTransition); + escapeByRef(e.e1, er); er.byFunc(e.func, false); } @@ -1629,14 +1613,14 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi void visitArrayLiteral(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isTypeSArray() || tb.isTypeDArray()) { if (e.basis) - escapeByValue(e.basis, er, retRefTransition); + escapeExp(e.basis, er, deref); foreach (el; *e.elements) { if (el) - escapeByValue(el, er, retRefTransition); + escapeExp(el, er, deref); } } } @@ -1648,120 +1632,127 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi foreach (ex; *e.elements) { if (ex) - escapeByValue(ex, er, retRefTransition); + escapeExp(ex, er, deref); } } + if (deref == -1) + { + er.byExp(e, er.inRetRefTransition > 0); // + } } void visitNew(NewExp e) { Type tb = e.newtype.toBasetype(); - if (tb.ty == Tstruct && !e.member && e.arguments) + if (tb.isTypeStruct() && !e.member && e.arguments) { foreach (ex; *e.arguments) { if (ex) - escapeByValue(ex, er, retRefTransition); + escapeExp(ex, er, deref); } } } void visitCast(CastExp e) { - if (!e.type.hasPointers()) + if (deref < 0 || !e.type.hasPointers()) return; Type tb = e.type.toBasetype(); - if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) - { - escapeByRef(e.e1, er, retRefTransition); - } + if (tb.isTypeDArray() && e.e1.type.toBasetype().isTypeSArray()) + escapeExp(e.e1, er, deref - 1); else - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref); } void visitSlice(SliceExp e) { - if (auto ve = e.e1.isVarExp()) - { - VarDeclaration v = ve.var.isVarDeclaration(); - Type tb = e.type.toBasetype(); - if (v) - { - if (tb.ty == Tsarray) - return; - if (v.isTypesafeVariadicArray) - { - er.byValue(v); - return; - } - } - } - Type t1b = e.e1.type.toBasetype(); - if (t1b.ty == Tsarray) - { - Type tb = e.type.toBasetype(); - if (tb.ty != Tsarray) - escapeByRef(e.e1, er, retRefTransition); - } - else - escapeByValue(e.e1, er, retRefTransition); + // Usually: slicing a static array escapes by ref, slicing a dynamic array escapes by value. + // However, slices with compile-time known length can implicitly converted to static arrays: + // int*[3] b = sa[0 .. 3]; + // So we need to compare the type before slicing and after slicing + const bool staticBefore = e.e1.type.toBasetype().isTypeSArray() !is null; + const bool staticAfter = e.type.toBasetype().isTypeSArray() !is null; + escapeExp(e.e1, er, deref + staticAfter - staticBefore); } void visitIndex(IndexExp e) { - if (e.e1.type.toBasetype().ty == Tsarray || - er.live && e.type.hasPointers()) + Type tb = e.e1.type.toBasetype(); + + if (tb.isTypeSArray()) + { + escapeExp(e.e1, er, deref); + } + else if (tb.isTypeDArray()) { - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref + 1); } } void visitBin(BinExp e) { - Type tb = e.type.toBasetype(); - if (tb.ty == Tpointer) + if (e.type.toBasetype().isTypePointer()) { - escapeByValue(e.e1, er, retRefTransition); - escapeByValue(e.e2, er, retRefTransition); + // The expression must be pointer arithmetic, e.g. `p + 1` or `1 + p` + escapeExp(e.e1, er, deref); + escapeExp(e.e2, er, deref); } } void visitBinAssign(BinAssignExp e) { - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref); } void visitAssign(AssignExp e) { - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref); } void visitComma(CommaExp e) { - escapeByValue(e.e2, er, retRefTransition); + escapeExp(e.e2, er, deref); } void visitCond(CondExp e) { - escapeByValue(e.e1, er, retRefTransition); - escapeByValue(e.e2, er, retRefTransition); + escapeExp(e.e1, er, deref); + escapeExp(e.e2, er, deref); } void visitCall(CallExp e) { //printf("CallExp(): %s\n", e.toChars()); - /* Check each argument that is - * passed as 'return scope'. - */ + // Check each argument that is passed as 'return scope'. TypeFunction tf = e.calledFunctionType(); - if (!tf || !e.type.hasPointers()) + if (!tf) + return; + + if (deref < 0 && !tf.isref) + { + er.byExp(e, er.inRetRefTransition > 0); + return; + } + + // A function may have a return scope struct parameter, but only return an `int` field of that struct + if (deref >= 0 && !e.type.hasPointers()) return; + /// Given a `scope` / `return scope` / `return ref` annotation, + /// get the corresponding pointer dereference level + static int paramDeref(ScopeRef psr) + { + return + (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) ? -1 : + (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) ? 0 : + +1; + } + if (e.arguments && e.arguments.length) { - /* j=1 if _arguments[] is first argument, - * skip it because it is not passed by ref - */ + // j=1 if _arguments[] is first argument, + // skip it because it is not passed by ref int j = tf.isDstyleVariadic(); for (size_t i = j; i < e.arguments.length; ++i) { @@ -1772,139 +1763,73 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi Parameter p = tf.parameterList[i - j]; const stc = tf.parameterStorageClass(null, p); ScopeRef psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (tf.isref) - { - /* ignore `ref` on struct constructor return because - * struct S { this(return scope int* q) { this.p = q; } int* p; } - * is different from: - * ref char* front(return scope char** q) { return *q; } - * https://github.com/dlang/dmd/pull/14869 - */ - if (auto dve = e.e1.isDotVarExp()) - if (auto fd = dve.var.isFuncDeclaration()) - if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct()) - { - escapeByValue(arg, er, retRefTransition); - } - } - else - escapeByValue(arg, er, true); - } - else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - { - if (tf.isref) - { - /* Treat: - * ref P foo(return ref P p) - * as: - * p; - */ - escapeByValue(arg, er, retRefTransition); - } - else - escapeByRef(arg, er, retRefTransition); - } + + // For struct constructors, `tf.isref` is true, but for escape analysis, + // it's as if they return `void` and escape through the first (`this`) parameter: + // void assign(ref S this, return scope constructorArgs...) + // If you then return the constructed result by value, it doesn't count + // as dereferencing the scope arguments, they're still escaped. + const isRef = tf.isref && !(tf.isctor && paramDeref(psr) == 0); + const maybeInaccurate = deref == 0 && paramDeref(psr) == 0; + er.inRetRefTransition += maybeInaccurate; + if (paramDeref(psr) <= 0) + escapeExp(arg, er, deref + paramDeref(psr) + isRef); + er.inRetRefTransition -= maybeInaccurate; } } } + // If 'this' is returned, check it too Type t1 = e.e1.type.toBasetype(); - if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction) + DotVarExp dve = e.e1.isDotVarExp(); + if (dve && t1.ty == Tfunction) { - DotVarExp dve = e.e1.isDotVarExp(); FuncDeclaration fd = dve.var.isFuncDeclaration(); - if (fd && fd.isThis()) - { - /* Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this` - */ - - /***************************** - * Concoct storage class for member function's implicit `this` parameter. - * Params: - * fd = member function - * Returns: - * storage class for fd's `this` - */ - StorageClass getThisStorageClass(FuncDeclaration fd) - { - StorageClass stc; - auto tf = fd.type.toBasetype().isTypeFunction(); - if (tf.isreturn) - stc |= STC.return_; - if (tf.isreturnscope) - stc |= STC.returnScope | STC.scope_; - auto ad = fd.isThis(); - if (ad.isClassDeclaration() || tf.isScopeQual) - stc |= STC.scope_; - if (ad.isStructDeclaration()) - stc |= STC.ref_; // `this` for a struct member function is passed by `ref` - return stc; - } - - const psr = buildScopeRef(getThisStorageClass(fd)); - if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (!tf.isref || tf.isctor) - escapeByValue(dve.e1, er, retRefTransition); - } - else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - { - if (tf.isref) - { - /* Treat calling: - * struct S { ref S foo() return; } - * as: - * this; - */ - escapeByValue(dve.e1, er, retRefTransition); - } - else - escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope); - } - } + if (!fd) + return; - // If it's also a nested function that is 'return scope' - if (fd && fd.isNested()) + // https://issues.dlang.org/show_bug.cgi?id=20149#c10 + if (deref < 0 && dve.var.isCtorDeclaration()) { - if (tf.isreturn && tf.isScopeQual) - { - if (tf.isreturnscope) - er.byFunc(fd, true); - else - er.byExp(e, false); - } + er.byExp(e, false); + return; } + + // Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this` + const psr = buildScopeRef(getThisStorageClass(fd)); + er.inRetRefTransition += (psr == ScopeRef.ReturnRef_Scope); + if (paramDeref(psr) <= 0) + escapeExp(dve.e1, er, deref + paramDeref(psr) + (tf.isref && !tf.isctor)); + er.inRetRefTransition -= (psr == ScopeRef.ReturnRef_Scope); } - /* If returning the result of a delegate call, the .ptr - * field of the delegate must be checked. - */ - if (t1.isTypeDelegate()) + // The return value of a delegate call with return (scope) may point to a closure variable, + // so escape the delegate in case it's `scope` / stack allocated. + if (t1.isTypeDelegate() && tf.isreturn) { - if (tf.isreturn) - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref + tf.isref); } - /* If it's a nested function that is 'return scope' - */ + // If `fd` is a nested function that's return ref / return scope, check that + // it doesn't escape closure vars if (auto ve = e.e1.isVarExp()) { - FuncDeclaration fd = ve.var.isFuncDeclaration(); - if (fd && fd.isNested()) + if (FuncDeclaration fd = ve.var.isFuncDeclaration()) { - if (tf.isreturn && tf.isScopeQual) + if (fd.isNested() && tf.isreturn) { - if (tf.isreturnscope) - er.byFunc(fd, true); - else - er.byExp(e, false); + er.byFunc(fd, true); } } } } + if (deref > 0 && !er.live) + return; // scope is not transitive currently, so dereferencing expressions don't escape + + if (deref >= 0 && er.live && !e.type.hasPointers()) + return; // can't escape non-pointer values by value + switch (e.op) { case EXP.address: return visitAddr(e.isAddrExp()); @@ -1929,14 +1854,36 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi case EXP.question: return visitCond(e.isCondExp()); case EXP.call: return visitCall(e.isCallExp()); default: - if (auto b = e.isBinExp()) - return visitBin(b); if (auto ba = e.isBinAssignExp()) return visitBinAssign(ba); + if (auto b = e.isBinExp()) + return visitBin(b); return visit(e); } } +/***************************** + * Concoct storage class for member function's implicit `this` parameter. + * Params: + * fd = member function + * Returns: + * storage class for fd's `this` + */ +StorageClass getThisStorageClass(FuncDeclaration fd) +{ + StorageClass stc; + auto tf = fd.type.toBasetype().isTypeFunction(); + if (tf.isreturn) + stc |= STC.return_; + if (tf.isreturnscope) + stc |= STC.returnScope | STC.scope_; + auto ad = fd.isThis(); + if ((ad && ad.isClassDeclaration()) || tf.isScopeQual) + stc |= STC.scope_; + if (ad && ad.isStructDeclaration()) + stc |= STC.ref_; // `this` for a struct member function is passed by `ref` + return stc; +} /**************************************** * e is an expression to be returned by 'ref'. @@ -1954,238 +1901,10 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi * Params: * e = expression to be returned by 'ref' * er = where to place collected data - * retRefTransition = if `e` is returned through a `return (ref) scope` function call */ -private -void escapeByRef(Expression e, ref scope EscapeByResults er, bool retRefTransition = false) +void escapeByRef(Expression e, ref scope EscapeByResults er) { - //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition); - void visit(Expression e) - { - } - - void visitVar(VarExp e) - { - auto v = e.var.isVarDeclaration(); - if (v) - { - if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init) - { - /* If compiler generated ref temporary - * (ref v = ex; ex) - * look at the initializer instead - */ - if (ExpInitializer ez = v._init.isExpInitializer()) - { - if (auto ce = ez.exp.isConstructExp()) - escapeByRef(ce.e2, er, retRefTransition); - else - escapeByRef(ez.exp, er, retRefTransition); - } - } - else - er.byRef(v, retRefTransition); - } - } - - void visitThis(ThisExp e) - { - if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) - escapeByValue(e, er, retRefTransition); - else if (e.var) - er.byRef(e.var, retRefTransition); - } - - void visitPtr(PtrExp e) - { - escapeByValue(e.e1, er, retRefTransition); - } - - void visitIndex(IndexExp e) - { - Type tb = e.e1.type.toBasetype(); - if (auto ve = e.e1.isVarExp()) - { - VarDeclaration v = ve.var.isVarDeclaration(); - if (v && v.isTypesafeVariadicArray) - { - er.byRef(v, retRefTransition); - return; - } - } - if (tb.ty == Tsarray) - { - escapeByRef(e.e1, er, retRefTransition); - } - else if (tb.ty == Tarray) - { - escapeByValue(e.e1, er, retRefTransition); - } - } - - void visitStructLiteral(StructLiteralExp e) - { - if (e.elements) - { - foreach (ex; *e.elements) - { - if (ex) - escapeByRef(ex, er, retRefTransition); - } - } - er.byExp(e, retRefTransition); - } - - void visitDotVar(DotVarExp e) - { - Type t1b = e.e1.type.toBasetype(); - if (t1b.ty == Tclass) - escapeByValue(e.e1, er, retRefTransition); - else - escapeByRef(e.e1, er, retRefTransition); - } - - void visitBinAssign(BinAssignExp e) - { - escapeByRef(e.e1, er, retRefTransition); - } - - void visitAssign(AssignExp e) - { - escapeByRef(e.e1, er, retRefTransition); - } - - void visitComma(CommaExp e) - { - escapeByRef(e.e2, er, retRefTransition); - } - - void visitCond(CondExp e) - { - escapeByRef(e.e1, er, retRefTransition); - escapeByRef(e.e2, er, retRefTransition); - } - - void visitCall(CallExp e) - { - //printf("escapeByRef.CallExp(): %s\n", e.toChars()); - /* If the function returns by ref, check each argument that is - * passed as 'return ref'. - */ - TypeFunction tf = e.calledFunctionType(); - if (!tf) - return; - if (tf.isref) - { - if (e.arguments && e.arguments.length) - { - /* j=1 if _arguments[] is first argument, - * skip it because it is not passed by ref - */ - int j = tf.isDstyleVariadic(); - for (size_t i = j; i < e.arguments.length; ++i) - { - Expression arg = (*e.arguments)[i]; - size_t nparams = tf.parameterList.length; - if (i - j < nparams && i >= j) - { - Parameter p = tf.parameterList[i - j]; - const stc = tf.parameterStorageClass(null, p); - ScopeRef psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(arg, er, retRefTransition); - else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (auto de = arg.isDelegateExp()) - { - if (de.func.isNested()) - er.byExp(de, false); - } - else - escapeByValue(arg, er, retRefTransition); - } - } - } - } - // If 'this' is returned by ref, check it too - Type t1 = e.e1.type.toBasetype(); - if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction) - { - DotVarExp dve = e.e1.isDotVarExp(); - - // https://issues.dlang.org/show_bug.cgi?id=20149#c10 - if (dve.var.isCtorDeclaration()) - { - er.byExp(e, false); - return; - } - - StorageClass stc = dve.var.storage_class & (STC.return_ | STC.scope_ | STC.ref_); - if (tf.isreturn) - stc |= STC.return_; - if (tf.isref) - stc |= STC.ref_; - if (tf.isScopeQual) - stc |= STC.scope_; - if (tf.isreturnscope) - stc |= STC.returnScope; - - const psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope); - else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - escapeByValue(dve.e1, er, retRefTransition); - - // If it's also a nested function that is 'return ref' - if (FuncDeclaration fd = dve.var.isFuncDeclaration()) - { - if (fd.isNested() && tf.isreturn) - { - er.byExp(e, false); - } - } - } - // If it's a delegate, check it too - if (e.e1.op == EXP.variable && t1.ty == Tdelegate) - { - escapeByValue(e.e1, er, retRefTransition); - } - - /* If it's a nested function that is 'return ref' - */ - if (auto ve = e.e1.isVarExp()) - { - FuncDeclaration fd = ve.var.isFuncDeclaration(); - if (fd && fd.isNested()) - { - if (tf.isreturn) - er.byExp(e, false); - } - } - } - else - er.byExp(e, retRefTransition); - } - - switch (e.op) - { - case EXP.variable: return visitVar(e.isVarExp()); - case EXP.this_: return visitThis(e.isThisExp()); - case EXP.star: return visitPtr(e.isPtrExp()); - case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp()); - case EXP.dotVariable: return visitDotVar(e.isDotVarExp()); - case EXP.index: return visitIndex(e.isIndexExp()); - case EXP.blit: return visitAssign(e.isBlitExp()); - case EXP.construct: return visitAssign(e.isConstructExp()); - case EXP.assign: return visitAssign(e.isAssignExp()); - case EXP.comma: return visitComma(e.isCommaExp()); - case EXP.question: return visitCond(e.isCondExp()); - case EXP.call: return visitCall(e.isCallExp()); - default: - if (auto ba = e.isBinAssignExp()) - return visitBinAssign(ba); - return visit(e); - } + escapeExp(e, er, -1); } /************************************ @@ -2213,6 +1932,20 @@ struct EscapeByResults void delegate(VarDeclaration, bool retRefTransition) byRef; /// called on variables with values containing pointers void delegate(VarDeclaration) byValue; + + /// Switch over `byValue` and `byRef` based on `deref` level (-1 = by ref, 0 = by value, 1 = only for live currently) + private void varDeref(VarDeclaration var, int deref) + { + if (var.isDataseg()) + return; + if (deref == -1) + byRef(var, inRetRefTransition > 0); + else if (deref == 0) + byValue(var); + else if (deref > 0 && live) + byValue(var); + } + /// called on nested functions that are turned into delegates /// When `called` is true, it means the delegate escapes variables /// from the closure through a call to it, while `false` means the @@ -2223,6 +1956,14 @@ struct EscapeByResults /// if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. bool live = false; + + /// Incremented / decremented every time an ambiguous return ref/scope parameter is checked. + /// See retRefTransition above. + private int inRetRefTransition = 0; + + /// When forwarding a temp var to its initializer, + /// keep track of the temp var to break endless loops + private VarDeclaration lastTemp = null; } /************************* @@ -2266,7 +2007,7 @@ public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* v * - `VarDeclaration` of a non-scope parameter it was assigned to * - `null` for no reason */ -private void notMaybeScope(VarDeclaration v, RootObject o) +private void doNotInferScope(VarDeclaration v, RootObject o) { if (v.maybeScope) { @@ -2276,23 +2017,6 @@ private void notMaybeScope(VarDeclaration v, RootObject o) } } -/*********************************** - * Turn off `maybeScope` for variable `v` if it's not a parameter. - * - * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`, - * which is now just `VarDeclaration.maybeScope`. - * This function should probably be removed in future refactors. - * - * Params: - * v = variable - * o = reason for it being turned off - */ -private void doNotInferScope(VarDeclaration v, RootObject o) -{ - if (!v.isParameter) - notMaybeScope(v, o); -} - /*********************************** * After semantic analysis of the function body, * try to infer `scope` / `return` on the parameters @@ -2305,48 +2029,21 @@ private void doNotInferScope(VarDeclaration v, RootObject o) public void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) { - - if (funcdecl.returnInprocess) - { - funcdecl.returnInprocess = false; - if (funcdecl.storage_class & STC.return_) - { - if (funcdecl.type == f) - f = cast(TypeFunction)f.copy(); - f.isreturn = true; - f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope); - if (funcdecl.storage_class & STC.returninferred) - f.isreturninferred = true; - } - } - - if (!funcdecl.inferScope) + if (!funcdecl.scopeInprocess) return; - funcdecl.inferScope = false; + funcdecl.scopeInprocess = false; - // Eliminate maybescope's + if (funcdecl.storage_class & STC.return_) { - // Create and fill array[] with maybe candidates from the `this` and the parameters - VarDeclaration[10] tmp = void; - size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0); - - import dmd.common.smallbuffer : SmallBuffer; - auto sb = SmallBuffer!VarDeclaration(dim, tmp[]); - VarDeclaration[] array = sb[]; - - size_t n = 0; - if (funcdecl.vthis) - array[n++] = funcdecl.vthis; - if (funcdecl.parameters) - { - foreach (v; *funcdecl.parameters) - { - array[n++] = v; - } - } - eliminateMaybeScopes(array[0 .. n]); + if (funcdecl.type == f) + f = cast(TypeFunction)f.copy(); + f.isreturn = true; + f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope); + if (funcdecl.storage_class & STC.returninferred) + f.isreturninferred = true; } + // Infer STC.scope_ if (funcdecl.parameters && !funcdecl.errors) { @@ -2370,61 +2067,6 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) } } -/********************************************** - * Have some variables that are maybescopes that were - * assigned values from other maybescope variables. - * Now that semantic analysis of the function is - * complete, we can finalize this by turning off - * maybescope for array elements that cannot be scope. - * - * $(TABLE2 Scope Table, - * $(THEAD `va`, `v`, =>, `va` , `v` ) - * $(TROW maybe, maybe, =>, scope, scope) - * $(TROW scope, scope, =>, scope, scope) - * $(TROW scope, maybe, =>, scope, scope) - * $(TROW maybe, scope, =>, scope, scope) - * $(TROW - , - , =>, - , - ) - * $(TROW - , maybe, =>, - , - ) - * $(TROW - , scope, =>, error, error) - * $(TROW maybe, - , =>, scope, - ) - * $(TROW scope, - , =>, scope, - ) - * ) - * Params: - * array = array of variables that were assigned to from maybescope variables - */ -private void eliminateMaybeScopes(VarDeclaration[] array) -{ - enum log = false; - if (log) printf("eliminateMaybeScopes()\n"); - bool changes; - do - { - changes = false; - foreach (va; array) - { - if (log) printf(" va = %s\n", va.toChars()); - if (!(va.maybeScope || va.isScope())) - { - if (va.maybes) - { - foreach (v; *va.maybes) - { - if (log) printf(" v = %s\n", v.toChars()); - if (v.maybeScope) - { - // v cannot be scope since it is assigned to a non-scope va - notMaybeScope(v, va); - if (!v.isReference()) - v.storage_class &= ~(STC.return_ | STC.returninferred); - changes = true; - } - } - } - } - } - } while (changes); -} - /************************************************ * Is type a reference to a mutable value? * @@ -2552,24 +2194,6 @@ private EnclosedBy enclosesLifetimeOf(VarDeclaration va, VarDeclaration v) return EnclosedBy.none; } -/*************************************** - * Add variable `v` to maybes[] - * - * When a maybescope variable `v` is assigned to a maybescope variable `va`, - * we cannot determine if `this` is actually scope until the semantic - * analysis for the function is completed. Thus, we save the data - * until then. - * Params: - * v = a variable with `maybeScope == true` that was assigned to `this` - */ -private void addMaybe(VarDeclaration va, VarDeclaration v) -{ - //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars()); - if (!va.maybes) - va.maybes = new VarDeclarations(); - va.maybes.push(v); -} - // `setUnsafePreview` partially evaluated for dip1000 public bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg, @@ -2599,7 +2223,7 @@ private bool checkScopeVarAddr(VarDeclaration v, Expression e, ref Scope sc, boo if (!v.isScope()) { - notMaybeScope(v, e); + doNotInferScope(v, e); return false; } diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index a69b64d6b784f068e56c54a985b1dac7a8cc216e..3d2ad471f21eed280f38a2446ea6ef557250d137 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -33,6 +33,7 @@ import dmd.dclass; import dmd.dcast; import dmd.delegatize; import dmd.denum; +import dmd.deps; import dmd.dimport; import dmd.dinterpret; import dmd.dmangle; @@ -547,7 +548,7 @@ private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) // https://issues.dlang.org/show_bug.cgi?id=12585 // Extract the side effect part if ue.e1 is comma. - if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() + if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() { /* Even if opDollar is needed, 'e1' should be evaluate only once. So * Rewrite: @@ -948,7 +949,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) SearchOptFlags flags = SearchOpt.all; Dsymbol s; - if (sc.flags & SCOPE.ignoresymbolvisibility) + if (sc.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; // First look in local scopes @@ -965,8 +966,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) FuncDeclaration f = s.isFuncDeclaration(); if (f) { - TemplateDeclaration td = getFuncTemplateDecl(f); - if (td) + if (TemplateDeclaration td = getFuncTemplateDecl(f)) { if (td.overroot) td = td.overroot; @@ -1351,7 +1351,7 @@ private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 e = new CallExp(loc, e, arguments); // https://issues.dlang.org/show_bug.cgi?id=24017 - if (sc.flags & SCOPE.debug_) + if (sc.debug_) e.isCallExp().inDebugStatement = true; e = e.expressionSemantic(sc); @@ -1865,7 +1865,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; // If the call has a pure parent, then the called func must be pure. @@ -1974,7 +1974,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; // allow violations inside typeof(expression) - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; // allow violations inside compile-time evaluated expressions and debug conditionals if (v.ident == Id.ctfe) return false; // magic variable never violates pure and safe @@ -2104,9 +2104,9 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & SCOPE.debug_) + if (sc.debug_) return false; - if ((sc.flags & SCOPE.ctfe) && sc.func) + if (sc.ctfe && sc.func) return false; if (!sc.func) @@ -2179,7 +2179,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; /* The original expressions (`new S(...)` or `new S[...]``) will be * verified instead. This is to keep errors related to the original code @@ -2391,7 +2391,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = tthis = dve.e1.type; goto Lfd; } - else if (sc && sc.flags & SCOPE.Cfile && e1.isVarExp() && !e2) + else if (sc && sc.inCfile && e1.isVarExp() && !e2) { // ImportC: do not implicitly call function if no ( ) are present } @@ -3724,7 +3724,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; - else if (e.type.isimaginary && sc.flags & SCOPE.Cfile) + else if (e.type.isimaginary && sc.inCfile) { /* Convert to core.stdc.config.complex */ @@ -3861,8 +3861,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (global.params.fixAliasThis) { - ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol(); - if (expDsym) + if (ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol()) { //printf("expDsym = %s\n", expDsym.exp.toChars()); result = expDsym.exp.expressionSemantic(sc); @@ -3906,7 +3905,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.ident == Id.ctfe) { - if (sc.flags & SCOPE.ctfe) + if (sc.ctfe) { error(exp.loc, "variable `__ctfe` cannot be read at compile time"); return setError(); @@ -4290,7 +4289,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } buffer.write4(0); e.setData(buffer.extractData(), newlen, 4); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tuns32.sarrayOf(e.len + 1); else e.type = Type.tdchar.immutableOf().arrayOf(); @@ -4315,7 +4314,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } buffer.writeUTF16(0); e.setData(buffer.extractData(), newlen, 2); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tuns16.sarrayOf(e.len + 1); else e.type = Type.twchar.immutableOf().arrayOf(); @@ -4327,7 +4326,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto default; default: - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tchar.sarrayOf(e.len + 1); else e.type = Type.tchar.immutableOf().arrayOf(); @@ -4526,7 +4525,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type t = cle.type.typeSemantic(cle.loc, sc); auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret); - auto e = initializerToExpression(init, t, (sc.flags & SCOPE.Cfile) != 0); + auto e = initializerToExpression(init, t, sc.inCfile); if (!e) { error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init)); @@ -5099,7 +5098,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } else if (sc.needsCodegen() && // interpreter doesn't need this lowered - !exp.onstack && !exp.type.isscope()) // these won't use the GC + !exp.onstack && !exp.type.isScopeClass()) // these won't use the GC { /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` * or `_d_newclassTTrace` @@ -5427,7 +5426,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression d = new DeclarationExp(e.loc, e.cd); sc = sc.push(); // just create new scope - sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE + sc.ctfe = false; // temporary stop CTFE d = d.expressionSemantic(sc); sc = sc.pop(); @@ -5595,7 +5594,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint olderrors; sc = sc.push(); // just create new scope - sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE + sc.ctfe = false; // temporary stop CTFE sc.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=12506 /* fd.treq might be incomplete type, @@ -5822,7 +5821,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -6005,7 +6004,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Rewrite (*fp)(arguments) to fp(arguments) exp.e1 = (cast(PtrExp)exp.e1).e1; } - else if (exp.e1.op == EXP.type && (sc && sc.flags & SCOPE.Cfile)) + else if (exp.e1.op == EXP.type && (sc && sc.inCfile)) { const numArgs = exp.arguments ? exp.arguments.length : 0; @@ -6622,7 +6621,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.f.checkNestedReference(sc, exp.loc)) return setError(); } - else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_))) + else if (sc.func && sc.intypeof != 1 && !(sc.ctfe || sc.debug_)) { bool err = false; if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1)) @@ -6886,7 +6885,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor VarDeclaration v = s.isVarDeclaration(); if (v) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* Do semantic() on the type before inserting v into the symbol table */ @@ -6920,7 +6919,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if (v && (sc.flags & SCOPE.Cfile)) + if (v && sc.inCfile) { /* Do semantic() on initializer last so this will be legal: * int a = a; @@ -6958,7 +6957,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // The mangling change only works for D mangling } - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) { /* https://issues.dlang.org/show_bug.cgi?id=21272 * If we are in a foreach body we need to extract the @@ -7096,7 +7095,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=23650 // We generate object code for typeinfo, required // by typeid, only if in non-speculative context - if (sc.flags & SCOPE.compile) + if (sc.traitsCompiles) { genObjCode = false; } @@ -7179,7 +7178,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IsExp::semantic(%s)\n", e.toChars()); } - if (e.id && !(sc.flags & SCOPE.condition)) + if (e.id && !sc.condition) { error(e.loc, "can only declare type aliases within `static if` conditionals or `static assert`s"); return setError(); @@ -7208,7 +7207,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Scope* sc2 = sc.copy(); // keep sc.flags sc2.tinst = null; sc2.minst = null; - sc2.flags |= SCOPE.fullinst; + sc2.fullinst = true; Type t = dmd.typesem.trySemantic(e.targ, e.loc, sc2); sc2.pop(); if (!t) // errors, so condition is false @@ -7723,29 +7722,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const slice = se.peekString(); message("file %.*s\t(%s)", cast(int)slice.length, slice.ptr, resolvedNamez.ptr); } - if (global.params.moduleDeps.buffer !is null) - { - OutBuffer* ob = global.params.moduleDeps.buffer; - Module imod = sc._module; - if (!global.params.moduleDeps.name) - ob.writestring("depsFile "); - ob.writestring(imod.toPrettyChars()); - ob.writestring(" ("); - escapePath(ob, imod.srcfile.toChars()); - ob.writestring(") : "); - if (global.params.moduleDeps.name) - ob.writestring("string : "); - ob.write(se.peekString()); - ob.writestring(" ("); - escapePath(ob, resolvedNamez.ptr); - ob.writestring(")"); - ob.writenl(); - } - if (global.params.makeDeps.doOutput) - { - global.params.makeDeps.files.push(resolvedNamez.ptr); - } + addImportExpDep(global.params.moduleDeps, global.params.makeDeps, resolvedNamez, se.peekString(), sc._module); { auto fileName = FileName(resolvedNamez); @@ -8110,7 +8088,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op)); } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -8503,7 +8481,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* Special handling for &"string"/&(T[]){0, 1} * since C regards string/array literals as lvalues @@ -8743,7 +8721,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_)) + if (sc.func && !sc.intypeof && !sc.debug_) { sc.setUnsafe(false, exp.loc, "`this` reference necessary to take address of member `%s` in `@safe` function `%s`", @@ -8834,7 +8812,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto case Terror; } - if (sc.flags & SCOPE.Cfile && exp.type && exp.type.toBasetype().ty == Tvoid) + if (sc.inCfile && exp.type && exp.type.toBasetype().ty == Tvoid) { // https://issues.dlang.org/show_bug.cgi?id=23752 // `&*((void*)(0))` is allowed in C @@ -9005,7 +8983,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (checkNonAssignmentArrayOp(e.e1)) return setError(); - e.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + e.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; result = e; } @@ -9084,7 +9062,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if ((sc && sc.flags & SCOPE.Cfile) && + if ((sc && sc.inCfile) && exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) && (exp.e1.op == EXP.address || exp.e1.op == EXP.star || exp.e1.op == EXP.uadd || exp.e1.op == EXP.negate)) @@ -9338,7 +9316,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.4-5: A cast does not yield an lvalue. * So ensure that castTo does not strip away the cast so that this @@ -9744,7 +9722,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } assert(!exp.type); - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -9842,7 +9820,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = e.e2.type; result = e; - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return; if (e.type is Type.tvoid) @@ -10141,7 +10119,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // OR it in, because it might already be set for C array indexing exp.indexIsInBounds |= bounds.contains(getIntRange(exp.e2)); } - else if (sc.flags & SCOPE.Cfile && t1b.ty == Tsarray) + else if (sc.inCfile && t1b.ty == Tsarray) { if (auto ve = exp.e1.isVarExp()) { @@ -10172,7 +10150,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -10348,7 +10326,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (auto e2comma = exp.e2.isCommaExp()) { - if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile)) + if (!e2comma.isGenerated && !sc.inCfile) error(exp.loc, "using the result of a comma expression is not allowed"); /* Rewrite to get rid of the comma from rvalue @@ -10493,7 +10471,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e1x = e; } - else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp()) + else if (sc.inCfile && e1x.isDotIdExp()) { auto die = e1x.isDotIdExp(); e1x = fieldLookup(die.e1, sc, die.ident, die.arrow); @@ -11088,7 +11066,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * string to match the size of e1. */ Type t2 = e2x.type.toBasetype(); - if (sc.flags & SCOPE.Cfile && e2x.isStringExp() && t2.isTypeSArray()) + if (sc.inCfile && e2x.isStringExp() && t2.isTypeSArray()) { uinteger_t dim1 = t1.isTypeSArray().dim.toInteger(); uinteger_t dim2 = t2.isTypeSArray().dim.toInteger(); @@ -13294,14 +13272,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e1x = resolveProperties(sc, e1x); e1x = e1x.toBoolean(sc); - if (sc.flags & SCOPE.condition) + if (sc.condition) { /* If in static if, don't evaluate e2 if we don't have to. */ e1x = e1x.optimize(WANTvalue); if (e1x.toBool().hasValue(exp.op == EXP.orOr)) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) result = new IntegerExp(exp.op == EXP.orOr); else result = IntegerExp.createBool(exp.op == EXP.orOr); @@ -13349,7 +13327,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e2x.type.ty == Tvoid) exp.type = Type.tvoid; else - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; exp.e1 = e1x; exp.e2 = e2x; @@ -13446,7 +13424,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (f1 || f2) return setError(); - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; // Special handling for array comparisons Expression arrayLowering = null; @@ -13741,7 +13719,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (f1 || f2) return setError(); - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; if (!isArrayComparison) { @@ -13957,7 +13935,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=23767 // `cast(void*) 0` should be treated as `null` so the ternary expression // gets the pointer type of the other branch - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { static void rewriteCNull(ref Expression e, ref Type t) { @@ -14273,7 +14251,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (Expression ex = unaSemantic(exp, sc)) return ex; - if (!(sc.flags & SCOPE.Cfile) && exp.ident == Id._mangleof) + if (!sc.inCfile && exp.ident == Id._mangleof) { // symbol.mangleof @@ -14409,7 +14387,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; } - const cfile = (sc.flags & SCOPE.Cfile) != 0; + const cfile = sc.inCfile; /* Special case: rewrite this.id and super.id * to be classtype.id and baseclasstype.id @@ -14464,13 +14442,13 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) */ if (ie.sds.isModule() && ie.sds != sc._module) flags |= SearchOpt.ignorePrivateImports; - if (sc.flags & SCOPE.ignoresymbolvisibility) + if (sc.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags); /* Check for visibility before resolving aliases because public * aliases to private symbols are public. */ - if (s && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc._module, s)) + if (s && !sc.ignoresymbolvisibility && !symbolIsVisible(sc._module, s)) { s = null; } @@ -15128,7 +15106,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) if (global.params.noSharedAccess != FeatureState.enabled || !sc || sc.intypeof || - sc.flags & SCOPE.ctfe) + sc.ctfe) { return false; } @@ -15699,7 +15677,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitStructLiteral(StructLiteralExp _this) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return _this; // C struct literals are lvalues else return visit(_this); @@ -15746,7 +15724,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action auto e1 = _this.e1; auto var = _this.var; //printf("DotVarExp::toLvalue(%s)\n", toChars()); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator * is an lvalue if the first expression is an lvalue. @@ -15792,7 +15770,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitCast(CastExp _this) { - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.4-5: A cast does not yield an lvalue. */ @@ -16295,7 +16273,7 @@ bool checkAddressable(Expression e, Scope* sc) continue; case EXP.variable: - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { // C11 6.5.3.2: A variable that has its address taken cannot be // stored in a register. @@ -16597,7 +16575,7 @@ Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc) return ErrorExp.get(); Expression e = new VarExp(loc, em); e = e.expressionSemantic(sc); - if (!(sc.flags & SCOPE.Cfile) && em.isCsymbol()) + if (!sc.inCfile && em.isCsymbol()) { /* C11 types them as int. But if in D file, * type qualified names as the enum @@ -16638,7 +16616,7 @@ Expression toBoolean(Expression exp, Scope* sc) case EXP.construct: case EXP.blit: case EXP.loweredAssignExp: - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return exp; // Things like: // if (a = b) ... @@ -16777,7 +16755,7 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool const uint nerrors = global.errors; sc = sc.startCTFE(); - sc.flags |= SCOPE.condition; + sc.condition = true; e = e.expressionSemantic(sc); e = resolveProperties(sc, e); diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 54ff82670aebbf87193dbe3e3097a108575fe34a..c815c589235660f53d97bf9d582315a6d2d0da04 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -119,9 +119,8 @@ private struct FUNCFLAG bool safetyInprocess; /// working on determining safety bool nothrowInprocess; /// working on determining nothrow bool nogcInprocess; /// working on determining @nogc - bool returnInprocess; /// working on inferring 'return' for parameters + bool scopeInprocess; /// infer `return` and `scope` for parameters bool inlineScanned; /// function has been scanned for inline possibilities - bool inferScope; /// infer 'scope' for parameters bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function @@ -431,8 +430,7 @@ extern (C++) class FuncDeclaration : Declaration { //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars()); assert(s != this); - AliasDeclaration ad = s.isAliasDeclaration(); - if (ad) + if (AliasDeclaration ad = s.isAliasDeclaration()) { if (overnext) return overnext.overloadInsert(ad); @@ -501,8 +499,7 @@ extern (C++) class FuncDeclaration : Declaration while (f && f.overnext) { //printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars()); - TemplateDeclaration td = f.overnext.isTemplateDeclaration(); - if (td) + if (TemplateDeclaration td = f.overnext.isTemplateDeclaration()) return td; f = f.overnext.isFuncDeclaration(); } @@ -724,19 +721,16 @@ extern (C++) class FuncDeclaration : Declaration if (!tf.isnogc) nogcInprocess = true; - if (!isVirtual() || this.isIntroducing()) - returnInprocess = true; - // Initialize for inferring STC.scope_ - inferScope = true; + scopeInprocess = true; } - extern (D) final uint flags() + extern (D) final uint saveFlags() { return bitFields; } - extern (D) final uint flags(uint f) + extern (D) final uint restoreFlags(uint f) { bitFields = f; return bitFields; @@ -1262,8 +1256,7 @@ extern (C++) class FuncDeclaration : Declaration { if (type) { - TypeFunction fdtype = type.isTypeFunction(); - if (fdtype) // Could also be TypeError + if (TypeFunction fdtype = type.isTypeFunction()) // Could also be TypeError return fdtype.parameterList; } @@ -1677,7 +1670,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration this.fes = fes; // Always infer scope for function literals // See https://issues.dlang.org/show_bug.cgi?id=20362 - this.inferScope = true; + this.scopeInprocess = true; //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars()); } @@ -1732,8 +1725,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration { if (parent) { - TemplateInstance ti = parent.isTemplateInstance(); - if (ti) + if (TemplateInstance ti = parent.isTemplateInstance()) return ti.tempdecl.toPrettyChars(QualifyTypes); } return Dsymbol.toPrettyChars(QualifyTypes); diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index 594e481e4694fce896b69b8cef2021f4e7e684f8..5d76cea9374ee5699dfceeb75c6f1ecc3ff9d0e6 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -214,11 +214,11 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration.isFinal()); - if (sc.flags & SCOPE.compile) + if (sc.traitsCompiles) funcdecl.skipCodegen = true; funcdecl._linkage = sc.linkage; - if (sc.flags & SCOPE.Cfile && funcdecl.isFuncLiteralDeclaration()) + if (sc.inCfile && funcdecl.isFuncLiteralDeclaration()) funcdecl._linkage = LINK.d; // so they are uniquely mangled if (auto fld = funcdecl.isFuncLiteralDeclaration()) @@ -263,7 +263,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) return null; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* C11 allows a function to be declared with a typedef, D does not. */ @@ -1047,8 +1047,7 @@ Ldone: } // If it's a member template - ClassDeclaration cd = ti.tempdecl.isClassMember(); - if (cd) + if (ClassDeclaration cd = ti.tempdecl.isClassMember()) { .error(funcdecl.loc, "%s `%s` cannot use template to add virtual function to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars()); } @@ -2182,7 +2181,7 @@ int getLevelAndCheck(FuncDeclaration fd, const ref Loc loc, Scope* sc, FuncDecla if (level != fd.LevelError) return level; // Don't give error if in template constraint - if (!(sc.flags & SCOPE.constraint)) + if (!sc.inTemplateConstraint) { const(char)* xstatic = fd.isStatic() ? "`static` " : ""; // better diagnostics for static functions @@ -2291,7 +2290,7 @@ bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc) if (!found) { //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); - if (!sc.intypeof && !(sc.flags & SCOPE.compile)) + if (!sc.intypeof && !sc.traitsCompiles) { fd.siblingCallers.push(fdthis); fd.computedEscapingSiblings = false; @@ -2777,7 +2776,7 @@ void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) */ bool isRootTraitsCompilesScope(Scope* sc) { - return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile); + return (sc.traitsCompiles) && !sc.func.skipCodegen; } /************************************** @@ -2801,7 +2800,7 @@ bool setUnsafe(Scope* sc, if (sc.intypeof) return false; // typeof(cast(int*)0) is safe - if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive + if (sc.debug_) // debug {} scopes are permissive return false; if (!sc.func) diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index c97aeb6d4d08fa86f37be0ca4eeca88100a7b3d3..2bdc1856e189d886a15f777054183d04e08692ba 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -248,6 +248,11 @@ extern (C++) struct Param const(char)[] exefile; const(char)[] mapfile; + // Time tracing + bool timeTrace = false; /// Whether profiling of compile time is enabled + uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report + const(char)* timeTraceFile; /// File path of output file + /// bool parsingUnittestsRequired() { diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index bd28d7be7b8be638865d254554a081c061980d2d..e2aa82a15ae1943085519ed04f0778629eda7971 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -250,6 +250,9 @@ struct Param DString resfile; DString exefile; DString mapfile; + bool timeTrace; + uint32_t timeTraceGranularityUs; + const char* timeTraceFile; }; struct structalign_t diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index a44fb2877a95b14003f4656f3d9f07fa169e4af4..7f2b225931bd966cff52c958f699035eac8c9d8a 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -2682,10 +2682,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitDotId(DotIdExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - if (e.arrow) - buf.writestring("->"); - else - buf.writeByte('.'); + buf.writeByte('.'); buf.writestring(e.ident.toString()); } @@ -3927,7 +3924,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te /* Use 'storage class' (prefix) style for attributes */ - if (t.mod) + if (t.mod && !(hgs.ddoc || hgs.hdrgen)) { MODtoBuffer(buf, t.mod); buf.writeByte(' '); @@ -3977,6 +3974,12 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te buf.writeByte(')'); } parametersToBuffer(t.parameterList, buf, hgs); + // postfix this attributes are more readable + if (t.mod && (hgs.ddoc || hgs.hdrgen)) + { + buf.writeByte(' '); + MODtoBuffer(buf, t.mod); + } if (t.isreturnscope && !t.isreturninferred) { buf.writestring(" return scope"); @@ -4273,13 +4276,23 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitTag(TypeTag t) { - if (t.mod & MODFlags.const_) - buf.writestring("const "); if (hgs.importcHdr && t.id) { + // https://issues.dlang.org/show_bug.cgi?id=24670 + // `const` must be parenthesized because it can be a return type + if (t.mod & MODFlags.const_) + buf.writestring("const("); + + // For C to D translation, `struct S` or `enum S` simply becomes `S` buf.writestring(t.id.toString()); + + if (t.mod & MODFlags.const_) + buf.writestring(")"); return; } + // The following produces something like "const enum E : short" + if (t.mod & MODFlags.const_) + buf.writestring("const "); buf.writestring(Token.toString(t.tok)); buf.writeByte(' '); if (t.id) diff --git a/gcc/d/dmd/importc.d b/gcc/d/dmd/importc.d index ece56c8d1bcd055b0b1c7066d19b8273a1fafb66..0413df03b5a9ddd44c6f52eee9db14d7bdeecdfd 100644 --- a/gcc/d/dmd/importc.d +++ b/gcc/d/dmd/importc.d @@ -41,7 +41,7 @@ import dmd.typesem; */ Type cAdjustParamType(Type t, Scope* sc) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return t; Type tb = t.toBasetype(); @@ -77,7 +77,7 @@ Type cAdjustParamType(Type t, Scope* sc) Expression arrayFuncConv(Expression e, Scope* sc) { //printf("arrayFuncConv() %s\n", e.toChars()); - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return e; auto t = e.type.toBasetype(); @@ -121,7 +121,6 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) if (e.isErrorExp()) return e; - Dsymbol s; auto t = e.type; if (t.isTypePointer()) { @@ -131,6 +130,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) error(e.loc, "since `%s` is a pointer, use `%s->%s` instead of `%s.%s`", pe, pe, id.toChars(), pe, id.toChars()); e = new PtrExp(e.loc, e); } + Dsymbol s; if (auto ts = t.isTypeStruct()) s = ts.sym.search(e.loc, id, 0); if (!s) @@ -154,7 +154,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) */ Expression carraySemantic(ArrayExp ae, Scope* sc) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return null; auto e1 = ae.e1.expressionSemantic(sc); diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index 8faad30f53918d46a464c6079a3f18a7b08f3d22..f48b3c69c67d6ce0acd41e36ba8bfc40a9180431 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -161,7 +161,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn // Convert initializer to Expression `ex` auto tm = fieldType.addMod(t.mod); auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret); - auto ex = iz.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + auto ex = iz.initializerToExpression(null, sc.inCfile); if (ex.op != EXP.error) i.value[j] = iz; return ex; @@ -305,7 +305,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn } if (auto tsa = t.isTypeSArray()) { - if (sc.flags & SCOPE.Cfile && tsa.isIncomplete()) + if (sc.inCfile && tsa.isIncomplete()) { // Change to array of known length auto tn = tsa.next.toBasetype(); @@ -369,7 +369,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn // If the result will be implicitly cast, move the cast into CTFE // to avoid premature truncation of polysemous types. // eg real [] x = [1.1, 2.2]; should use real precision. - if (i.exp.implicitConvTo(t) && !(sc.flags & SCOPE.Cfile)) + if (i.exp.implicitConvTo(t) && !sc.inCfile) { i.exp = i.exp.implicitCastTo(sc, t); } @@ -377,7 +377,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn { return i; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* the interpreter turns (char*)"string" into &"string"[0] which then * it cannot interpret. Resolve that case by doing optimize() first @@ -450,7 +450,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn /* Lop off terminating 0 of initializer for: * static char s[5] = "hello"; */ - if (sc.flags & SCOPE.Cfile && + if (sc.inCfile && typeb.ty == Tsarray && tynto.isSomeChar && tb.isTypeSArray().dim.toInteger() + 1 == typeb.isTypeSArray().dim.toInteger()) @@ -463,7 +463,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn * Initialize an array of unknown size with a string. * Change to static array of known size */ - if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + if (sc.inCfile && i.exp.isStringExp() && tb.isTypeSArray() && tb.isTypeSArray().isIncomplete()) { StringExp se = i.exp.isStringExp(); @@ -549,7 +549,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn { i.exp = i.exp.implicitCastTo(sc, t); } - else if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + else if (sc.inCfile && i.exp.isStringExp() && tta && (tta.next.ty == Tint8 || tta.next.ty == Tuns8) && ti.ty == Tsarray && ti.nextOf().ty == Tchar) { diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 26a56c2b1fd1cedcefdd396eacc0ba9cec05c1ef..2a11f30801799c3da7f5b33404f225ac92f86349 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -499,7 +499,7 @@ class Lexer clexerCharConstant(*t, c); return; } - else if (p[1] == '\"') // C wide string literal + if (p[1] == '\"') // C wide string literal { const c = *p; ++p; @@ -509,7 +509,7 @@ class Lexer 'd'; return; } - else if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal + if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal { p += 2; escapeStringConstant(t); @@ -542,14 +542,13 @@ class Lexer delimitedStringConstant(t); return; } - else if (p[1] == '{') + if (p[1] == '{') { p++; tokenStringConstant(t); return; } - else - goto case_ident; + goto case_ident; case 'i': if (Ccompile) goto case_ident; @@ -559,20 +558,19 @@ class Lexer escapeStringConstant(t, true); return; } - else if (p[1] == '`') + if (p[1] == '`') { p++; // skip the i wysiwygStringConstant(t, true); return; } - else if (p[1] == 'q' && p[2] == '{') + if (p[1] == 'q' && p[2] == '{') { p += 2; // skip the i and q tokenStringConstant(t, true); return; } - else - goto case_ident; + goto case_ident; case '"': escapeStringConstant(t); return; @@ -894,7 +892,7 @@ class Lexer t.value = TOK.comment; return; } - else if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr) + if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr) { // if /** but not /**/ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 6a2d349f15b5e6b2f581920024237b94f7bb1f2f..41a645e8b9fc8b10fb4b954b7f05925c30ad9f3e 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -21,7 +21,6 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.denum; @@ -58,6 +57,11 @@ static if (__VERSION__ < 2095) private alias StringValueType = StringValue!Type; } +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + /*************************** * Return !=0 if modfrom can be implicitly converted to modto */ @@ -67,10 +71,6 @@ bool MODimplicitConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe return true; //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto); - auto X(T, U)(T m, U n) - { - return ((m << 4) | n); - } switch (X(modfrom & ~MODFlags.shared_, modto & ~MODFlags.shared_)) { @@ -98,11 +98,6 @@ MATCH MODmethodConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe if (MODimplicitConv(modfrom, modto)) return MATCH.constant; - auto X(T, U)(T m, U n) - { - return ((m << 4) | n); - } - switch (X(modfrom, modto)) { case X(0, MODFlags.wild): @@ -682,7 +677,7 @@ extern (C++) abstract class Type : ASTNode return false; } - bool isscope() + bool isScopeClass() { return false; } @@ -1282,24 +1277,6 @@ extern (C++) abstract class Type : ASTNode return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this; } - /******************************* - * Determine if converting 'this' to 'to' is an identity operation, - * a conversion to const operation, or the types aren't the same. - * Returns: - * MATCH.exact 'this' == 'to' - * MATCH.constant 'to' is const - * MATCH.nomatch conversion to mutable or invariant - */ - MATCH constConv(Type to) - { - //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to.toChars()); - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - /*************************************** * Compute MOD bits matching `this` argument type to wild parameter type. * Params: @@ -1362,12 +1339,6 @@ extern (C++) abstract class Type : ASTNode return defaultInit(this, loc); } - // if initializer is 0 - bool isZeroInit(const ref Loc loc) - { - return false; // assume not - } - /*************************************** * Return !=0 if the type or any of its subtypes is wild. */ @@ -1866,35 +1837,6 @@ extern (C++) abstract class TypeNext : Type return t; } - override MATCH constConv(Type to) - { - //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to.toChars()); - if (equals(to)) - return MATCH.exact; - - if (!(ty == to.ty && MODimplicitConv(mod, to.mod))) - return MATCH.nomatch; - - Type tn = to.nextOf(); - if (!(tn && next.ty == tn.ty)) - return MATCH.nomatch; - - MATCH m; - if (to.isConst()) // whole tail const conversion - { - // Recursive shared level check - m = next.constConv(tn); - if (m == MATCH.exact) - m = MATCH.constant; - } - else - { - //printf("\tnext => %s, to.next => %s\n", next.toChars(), tn.toChars()); - m = next.equals(tn) ? MATCH.constant : MATCH.nomatch; - } - return m; - } - override final MOD deduceWild(Type t, bool isRef) { if (ty == Tfunction) @@ -2125,28 +2067,6 @@ extern (C++) final class TypeBasic : Type return (flags & TFlags.unsigned) != 0; } - override bool isZeroInit(const ref Loc loc) - { - switch (ty) - { - case Tchar: - case Twchar: - case Tdchar: - case Timaginary32: - case Timaginary64: - case Timaginary80: - case Tfloat32: - case Tfloat64: - case Tfloat80: - case Tcomplex32: - case Tcomplex64: - case Tcomplex80: - return false; // no - default: - return true; // yes - } - } - override bool hasUnsafeBitpatterns() { return ty == Tbool; @@ -2246,11 +2166,6 @@ extern (C++) final class TypeVector : Type return tb; } - override bool isZeroInit(const ref Loc loc) - { - return basetype.isZeroInit(loc); - } - override void accept(Visitor v) { v.visit(this); @@ -2327,26 +2242,11 @@ extern (C++) final class TypeSArray : TypeArray return nty.isSomeChar; } - override bool isZeroInit(const ref Loc loc) - { - return next.isZeroInit(loc); - } - override structalign_t alignment() { return next.alignment(); } - override MATCH constConv(Type to) - { - if (auto tsa = to.isTypeSArray()) - { - if (!dim.equals(tsa.dim)) - return MATCH.nomatch; - } - return TypeNext.constConv(to); - } - override Expression defaultInitLiteral(const ref Loc loc) { static if (LOGDEFAULTINIT) @@ -2445,11 +2345,6 @@ extern (C++) final class TypeDArray : TypeArray return nty.isSomeChar; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; @@ -2496,28 +2391,11 @@ extern (C++) final class TypeAArray : TypeArray return result; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; } - override MATCH constConv(Type to) - { - if (auto taa = to.isTypeAArray()) - { - MATCH mindex = index.constConv(taa.index); - MATCH mkey = next.constConv(taa.next); - // Pick the worst match - return mkey < mindex ? mkey : mindex; - } - return Type.constConv(to); - } - override void accept(Visitor v) { v.visit(this); @@ -2554,28 +2432,11 @@ extern (C++) final class TypePointer : TypeNext return result; } - override MATCH constConv(Type to) - { - if (next.ty == Tfunction) - { - if (to.nextOf() && next.equals((cast(TypeNext)to).next)) - return Type.constConv(to); - else - return MATCH.nomatch; - } - return TypeNext.constConv(to); - } - override bool isscalar() { return true; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2608,11 +2469,6 @@ extern (C++) final class TypeReference : TypeNext return result; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2868,16 +2724,6 @@ extern (C++) final class TypeFunction : TypeNext return newArgs; } - /** Extends TypeNext.constConv by also checking for matching attributes **/ - override MATCH constConv(Type to) - { - // Attributes need to match exactly, otherwise it's an implicit conversion - if (this.ty != to.ty || !this.attributesEqual(cast(TypeFunction) to)) - return MATCH.nomatch; - - return super.constConv(to); - } - extern (D) bool checkRetType(const ref Loc loc) { Type tb = next.toBasetype(); @@ -2994,11 +2840,6 @@ extern (C++) final class TypeDelegate : TypeNext return target.ptrsize; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; @@ -3392,13 +3233,6 @@ extern (C++) final class TypeStruct : Type return structinit; } - override bool isZeroInit(const ref Loc loc) - { - // Determine zeroInit here, as this can be called before semantic2 - sym.determineSize(sym.loc); - return sym.zeroInit; - } - override bool isAssignable() { bool assignable = true; @@ -3493,77 +3327,6 @@ extern (C++) final class TypeStruct : Type return sym.hasInvariant() || sym.hasFieldWithInvariant; } - extern (D) MATCH implicitConvToWithoutAliasThis(Type to) - { - //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars()); - - auto tos = to.isTypeStruct(); - if (!(tos && sym == tos.sym)) - return MATCH.nomatch; - - if (mod == to.mod) - return MATCH.exact; - - if (MODimplicitConv(mod, to.mod)) - return MATCH.constant; - - /* Check all the fields. If they can all be converted, - * allow the conversion. - */ - MATCH m = MATCH.constant; - uint offset = ~0; // must never match a field offset - foreach (v; sym.fields[]) - { - /* Why are we only looking at the first member of a union? - * The check should check for overlap of v with the previous field, - * not just starting at the same point - */ - if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field - continue; // ignore - - Type tvf = v.type.addMod(mod); // from type - Type tvt = v.type.addMod(to.mod); // to type - - // field match - MATCH mf = tvf.implicitConvTo(tvt); - //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf); - - if (mf == MATCH.nomatch) - return MATCH.nomatch; - if (mf < m) // if field match is worse - m = mf; - offset = v.offset; - } - return m; - } - - extern (D) MATCH implicitConvToThroughAliasThis(Type to) - { - auto tos = to.isTypeStruct(); - if (!(tos && sym == tos.sym) && - sym.aliasthis && - !(att & AliasThisRec.tracing)) - { - if (auto ato = aliasthisOf(this)) - { - att = cast(AliasThisRec)(att | AliasThisRec.tracing); - MATCH m = ato.implicitConvTo(to); - att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); - return m; - } - } - return MATCH.nomatch; - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - override MOD deduceWild(Type t, bool isRef) { if (ty == t.ty && sym == (cast(TypeStruct)t).sym) @@ -3690,15 +3453,6 @@ extern (C++) final class TypeEnum : Type return memType().needsNested(); } - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - extern (D) Type toBasetype2() { if (!sym.members && !sym.memtype) @@ -3707,11 +3461,6 @@ extern (C++) final class TypeEnum : Type return tb.castMod(mod); // retain modifier bits from 'this' } - override bool isZeroInit(const ref Loc loc) - { - return sym.getDefaultValue(loc).toBool().hasValue(false); - } - override bool hasVoidInitPointers() { return memType().hasVoidInitPointers(); @@ -3767,58 +3516,6 @@ extern (C++) final class TypeClass : Type return sym; } - extern (D) MATCH implicitConvToWithoutAliasThis(Type to) - { - ClassDeclaration cdto = to.isClassHandle(); - MATCH m = constConv(to); - if (m > MATCH.nomatch) - return m; - - if (cdto && cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod)) - { - //printf("'to' is base\n"); - return MATCH.convert; - } - return MATCH.nomatch; - } - - extern (D) MATCH implicitConvToThroughAliasThis(Type to) - { - MATCH m; - if (sym.aliasthis && !(att & AliasThisRec.tracing)) - { - if (auto ato = aliasthisOf(this)) - { - att = cast(AliasThisRec)(att | AliasThisRec.tracing); - m = ato.implicitConvTo(to); - att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); - } - } - return m; - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - - /* Conversion derived to const(base) - */ - int offset = 0; - if (to.isBaseOf(this, &offset) && offset == 0 && MODimplicitConv(mod, to.mod)) - { - // Disallow: - // derived to base - // inout(derived) to inout(base) - if (!to.isMutable() && !to.isWild()) - return MATCH.convert; - } - - return MATCH.nomatch; - } - override MOD deduceWild(Type t, bool isRef) { ClassDeclaration cd = t.isClassHandle(); @@ -3840,12 +3537,7 @@ extern (C++) final class TypeClass : Type return wm; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - - override bool isscope() + override bool isScopeClass() { return sym.stack; } @@ -4088,12 +3780,6 @@ extern (C++) final class TypeNoreturn : Type return this; } - override MATCH constConv(Type to) - { - // Either another noreturn or conversion to any type - return this.implicitConvTo(to); - } - override bool isBoolean() { return true; // bottom type can be implicitly converted to any other type diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index 11c27c67abadd82e8bf789f51dc51c59f63b0b89..2ec4beeb6eb6ef12424a03dadcbc8c5e6042bfe7 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -239,7 +239,7 @@ public: virtual bool iscomplex(); virtual bool isscalar(); virtual bool isunsigned(); - virtual bool isscope(); + virtual bool isScopeClass(); virtual bool isString(); virtual bool isAssignable(); virtual bool isBoolean(); @@ -264,13 +264,11 @@ public: virtual Type *makeSharedWildConst(); virtual Type *makeMutable(); Type *toBasetype(); - virtual MATCH constConv(Type *to); virtual unsigned char deduceWild(Type *t, bool isRef); virtual ClassDeclaration *isClassHandle(); virtual structalign_t alignment(); virtual Expression *defaultInitLiteral(const Loc &loc); - virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0 virtual int hasWild() const; virtual bool hasVoidInitPointers(); virtual bool hasUnsafeBitpatterns(); @@ -340,7 +338,6 @@ public: Type *makeSharedWild() override final; Type *makeSharedWildConst() override final; Type *makeMutable() override final; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override final; void transitive(); void accept(Visitor *v) override { v->visit(this); } @@ -362,7 +359,6 @@ public: bool iscomplex() override; bool isscalar() override; bool isunsigned() override; - bool isZeroInit(const Loc &loc) override; // For eliminating dynamic_cast TypeBasic *isTypeBasic() override; @@ -385,7 +381,6 @@ public: bool isBoolean() override; Expression *defaultInitLiteral(const Loc &loc) override; TypeBasic *elementType(); - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -407,9 +402,7 @@ public: bool isIncomplete(); unsigned alignsize() override; bool isString() override; - bool isZeroInit(const Loc &loc) override; structalign_t alignment() override; - MATCH constConv(Type *to) override; Expression *defaultInitLiteral(const Loc &loc) override; bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; @@ -429,7 +422,6 @@ public: TypeDArray *syntaxCopy() override; unsigned alignsize() override; bool isString() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -444,9 +436,7 @@ public: static TypeAArray *create(Type *t, Type *index); const char *kind() override; TypeAArray *syntaxCopy() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; - MATCH constConv(Type *to) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -457,9 +447,7 @@ public: static TypePointer *create(Type *t); const char *kind() override; TypePointer *syntaxCopy() override; - MATCH constConv(Type *to) override; bool isscalar() override; - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -469,7 +457,6 @@ class TypeReference final : public TypeNext public: const char *kind() override; TypeReference *syntaxCopy() override; - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -557,7 +544,6 @@ public: bool hasLazyParameters(); bool isDstyleVariadic() const; - MATCH constConv(Type *to) override; bool isnothrow() const; void isnothrow(bool v); @@ -599,7 +585,6 @@ public: const char *kind() override; TypeDelegate *syntaxCopy() override; unsigned alignsize() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -709,7 +694,6 @@ public: TypeStruct *syntaxCopy() override; structalign_t alignment() override; Expression *defaultInitLiteral(const Loc &loc) override; - bool isZeroInit(const Loc &loc) override; bool isAssignable() override; bool isBoolean() override; bool needsDestruction() override; @@ -718,7 +702,6 @@ public: bool hasVoidInitPointers() override; bool hasUnsafeBitpatterns() override; bool hasInvariant() override; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override; void accept(Visitor *v) override { v->visit(this); } @@ -746,8 +729,6 @@ public: bool needsDestruction() override; bool needsCopyOrPostblit() override; bool needsNested() override; - MATCH constConv(Type *to) override; - bool isZeroInit(const Loc &loc) override; bool hasVoidInitPointers() override; bool hasUnsafeBitpatterns() override; bool hasInvariant() override; @@ -766,10 +747,8 @@ public: const char *kind() override; TypeClass *syntaxCopy() override; ClassDeclaration *isClassHandle() override; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override; - bool isZeroInit(const Loc &loc) override; - bool isscope() override; + bool isScopeClass() override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -820,7 +799,6 @@ class TypeNoreturn final : public Type public: const char *kind() override; TypeNoreturn *syntaxCopy() override; - MATCH constConv(Type* to) override; bool isBoolean() override; unsigned alignsize() override; @@ -847,6 +825,7 @@ namespace dmd bool equivalent(Type *src, Type *t); Covariant covariant(Type *, Type *, StorageClass * = nullptr, bool = false); bool isBaseOf(Type *tthis, Type *t, int *poffset); + bool isZeroInit(Type *t, const Loc &loc = Loc()); Type *trySemantic(Type *type, const Loc &loc, Scope *sc); Type *pointerTo(Type *type); Type *referenceTo(Type *type); @@ -873,4 +852,5 @@ namespace dmd uinteger_t size(Type *type); uinteger_t size(Type *type, const Loc &loc); MATCH implicitConvTo(Type* from, Type* to); + MATCH constConv(Type* from, Type* to); } diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d index 9e45e4549ffb6dbd7e5a94a8511a081f0ecd173d..02f47f8ac4b24047c5f78cdf5eb751b7055992aa 100644 --- a/gcc/d/dmd/nogc.d +++ b/gcc/d/dmd/nogc.d @@ -201,7 +201,7 @@ public: Expression checkGC(Scope* sc, Expression e) { - if (sc.flags & SCOPE.ctfeBlock) // ignore GC in ctfe blocks + if (sc.ctfeBlock) // ignore GC in ctfe blocks return e; /* If betterC, allow GC to happen in non-CTFE code. @@ -211,10 +211,10 @@ Expression checkGC(Scope* sc, Expression e) const betterC = !global.params.useGC; FuncDeclaration f = sc.func; if (e && e.op != EXP.error && f && sc.intypeof != 1 && - (!(sc.flags & SCOPE.ctfe) || betterC) && + (!sc.ctfe || betterC) && (f.type.ty == Tfunction && (cast(TypeFunction)f.type).isnogc || f.nogcInprocess || global.params.v.gc) && - !(sc.flags & SCOPE.debug_)) + !sc.debug_) { scope NOGCVisitor gcv = new NOGCVisitor(f); gcv.checkOnly = betterC; diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d index 756caf870bcb3a81cf9286bdd6741a0bede6667c..ed7323323802555984e14e05bc71253d5c6b5359 100644 --- a/gcc/d/dmd/ob.d +++ b/gcc/d/dmd/ob.d @@ -2001,7 +2001,7 @@ void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar) true, ); - escapeByValue(e, er, true); + escapeByValue(e, er); } /*************************************** diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index 0d32d7d9713d1099e4bd32f1ca4d361b78d04a37..22fbbc4fb8367e3a82271bf7a6ffc244d67d5ab6 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -564,8 +564,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { //printf("CastExp::op_overload() (%s)\n", e.toChars()); Expression result; - AggregateDeclaration ad = isAggregate(e.e1.type); - if (ad) + if (AggregateDeclaration ad = isAggregate(e.e1.type)) { Dsymbol fd = null; /* Rewrite as: @@ -1034,7 +1033,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); auto sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; Expression r = e.expressionSemantic(sc2); sc2.pop(); return r; @@ -1412,8 +1411,7 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres { assert(d); Expression e; - Declaration decl = d.isDeclaration(); - if (decl) + if (Declaration decl = d.isDeclaration()) e = new DotVarExp(loc, ethis, decl, false); else e = new DotIdExp(loc, ethis, d.ident); @@ -1427,8 +1425,7 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres */ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) { - Dsymbol s = ad.search(Loc.initial, funcid); - if (s) + if (Dsymbol s = ad.search(Loc.initial, funcid)) { //printf("search_function: s = '%s'\n", s.kind()); Dsymbol s2 = s.toAlias(); @@ -1436,8 +1433,7 @@ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) FuncDeclaration fd = s2.isFuncDeclaration(); if (fd && fd.type.ty == Tfunction) return fd; - TemplateDeclaration td = s2.isTemplateDeclaration(); - if (td) + if (TemplateDeclaration td = s2.isTemplateDeclaration()) return td; } return null; diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index 1fbe0ae360974b16c192fd5e839e922043967f10..fb9a18b2892d05c3c66e6d774119523445f3abbb 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -591,13 +591,13 @@ nothrow: /*************************** * Free returned value with FileName::free() */ - extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) + extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) pure { return defaultExt(name.toDString, ext.toDString).ptr; } /// Ditto - extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) + extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) pure { auto e = FileName.ext(name); if (e.length) // it already has an extension @@ -615,13 +615,13 @@ nothrow: /*************************** * Free returned value with FileName::free() */ - extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) + extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) pure { return forceExt(name.toDString, ext.toDString).ptr; } /// Ditto - extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) + extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) pure { if (auto e = FileName.ext(name)) return addExt(name[0 .. $ - e.length - 1], ext); diff --git a/gcc/d/dmd/root/rmem.d b/gcc/d/dmd/root/rmem.d index c6986c0b56fecec0898b03b7b5ab73af387c0bc5..8eaf6094f52c4bda652ad93a695873cbc88d1340 100644 --- a/gcc/d/dmd/root/rmem.d +++ b/gcc/d/dmd/root/rmem.d @@ -149,6 +149,7 @@ enum CHUNK_SIZE = (256 * 4096 - 64); __gshared size_t heapleft = 0; __gshared void* heapp; +__gshared size_t heapTotal = 0; // Total amount of memory allocated using malloc extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc { @@ -167,11 +168,13 @@ extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc if (m_size > CHUNK_SIZE) { + heapTotal += m_size; return Mem.check(malloc(m_size)); } heapleft = CHUNK_SIZE; heapp = Mem.check(malloc(CHUNK_SIZE)); + heapTotal += CHUNK_SIZE; goto L1; } diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index f1bd6c98c80fced7c3c45dba45ae3fce43c14b95..b0eb3d1abfaffd85c7c69d779c5998d706b0b492 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -128,8 +128,8 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) if (hasPointers && v.type.toBasetype().ty != Tstruct) { - if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize || - (v.offset & (target.ptrsize - 1)))) + if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) || + (v.offset & (target.ptrsize - 1))) { if (sc.setUnsafe(!printmsg, e.loc, "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v)) diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index f36a14ba05182cd5061415e35c4036d76da2ad4b..7983a7ac93f2ed5c1905cd67ccfb44289e34e5ba 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -40,33 +40,15 @@ enum class CSX : uint16_t halt = 0x20, // assert(0) }; -enum class SCOPE +enum class Contract : uint8_t { - // Flags that would not be inherited beyond scope nesting - ctor = 0x0001, // constructor type - noaccesscheck = 0x0002, // don't do access checks - condition = 0x0004, // inside static if/assert condition - debug_ = 0x0008, // inside debug conditional - - // Flags that would be inherited beyond scope nesting - constraint = 0x0010, // inside template constraint - invariant_ = 0x0020, // inside invariant code - require = 0x0040, // inside in contract code - ensure = 0x0060, // inside out contract code - contract = 0x0060, // [mask] we're inside contract code - ctfe = 0x0080, // inside a ctfe-only expression - compile = 0x0100, // inside __traits(compile) - ignoresymbolvisibility = 0x0200, // ignore symbol visibility (Bugzilla 15907) - - Cfile = 0x0800, // C semantics apply - free = 0x8000, // is on free list - fullinst = 0x10000, // fully instantiate templates - ctfeBlock = 0x20000, // inside a `if (__ctfe)` block - dip1000 = 0x40000, // dip1000 errors enabled for this scope - dip25 = 0x80000, // dip25 errors enabled for this scope + none = 0u, + invariant_ = 1u, + require = 2u, + ensure = 3u, }; -struct Scope +struct Scope final { Scope *enclosing; // enclosing Scope @@ -122,6 +104,37 @@ struct Scope unsigned flags; + bool ctor() const; + bool ctor(bool v); + bool noAccessCheck() const; + bool noAccessCheck(bool v); + bool condition() const; + bool condition(bool v); + bool debug_() const; + bool debug_(bool v); + bool inTemplateConstraint() const; + bool inTemplateConstraint(bool v); + Contract contract() const; + Contract contract(Contract v); + bool ctfe() const; + bool ctfe(bool v); + bool traitsCompiles() const; + bool traitsCompiles(bool v); + bool ignoresymbolvisibility() const; + bool ignoresymbolvisibility(bool v); + bool inCfile() const; + bool inCfile(bool v); + bool canFree() const; + bool canFree(bool v); + bool fullinst() const; + bool fullinst(bool v); + bool ctfeBlock() const; + bool ctfeBlock(bool v); + bool dip1000() const; + bool dip1000(bool v); + bool dip25() const; + bool dip25(bool v); + UserAttributeDeclaration *userAttribDecl; // user defined attributes DocComment *lastdc; // documentation comment for last symbol at this scope diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index 06a5eef47bf18a02f41fae9f11f4017eb81ccf54..fb7373c73f7bd7d2848a1f6b61046f2c35e54fd7 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -21,6 +21,7 @@ import dmd.astcodegen; import dmd.astenums; import dmd.attrib; import dmd.blockexit; +import dmd.timetrace; import dmd.clone; import dmd.dcast; import dmd.dclass; @@ -56,6 +57,7 @@ import dmd.parse; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; +import dmd.root.string : toDString; import dmd.rootobject; import dmd.root.utf; import dmd.sideeffect; @@ -118,43 +120,7 @@ private extern(C++) final class Semantic2Visitor : Visitor else if (result) return; - if (sa.msgs) - { - OutBuffer msgbuf; - for (size_t i = 0; i < sa.msgs.length; i++) - { - Expression e = (*sa.msgs)[i]; - sc = sc.startCTFE(); - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - sc = sc.endCTFE(); - e = ctfeInterpretForPragmaMsg(e); - if (e.op == EXP.error) - { - errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars()); - return; - } - StringExp se = e.toStringExp(); - if (se) - { - const slice = se.toUTF8(sc).peekString(); - // Hack to keep old formatting to avoid changing error messages everywhere - if (sa.msgs.length == 1) - msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr); - else - msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr); - } - else - msgbuf.printf("%s", e.toChars()); - } - error(sa.loc, "static assert: %s", msgbuf.extractChars()); - } - else - error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); - if (sc.tinst) - sc.tinst.printInstantiationTrace(); - if (!global.gag) - fatal(); + staticAssertFail(sa, sc); } override void visit(TemplateInstance tempinst) @@ -282,7 +248,7 @@ private extern(C++) final class Semantic2Visitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=14166 // https://issues.dlang.org/show_bug.cgi?id=20417 // Don't run CTFE for the temporary variables inside typeof or __traits(compiles) - vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret); + vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.traitsCompiles ? INITnointerpret : INITinterpret); lowerStaticAAs(vd, sc); vd.inuse--; } @@ -330,7 +296,7 @@ private extern(C++) final class Semantic2Visitor : Visitor { // Cannot initialize a thread-local class or pointer to struct variable with a literal // that itself is a thread-local reference and would need dynamic initialization also. - if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared()) + if (vd.type.ty == Tclass && vd.type.isMutable() && !vd.type.isShared()) { ExpInitializer ei = vd._init.isExpInitializer(); if (ei && ei.exp.op == EXP.classReference) @@ -393,6 +359,9 @@ private extern(C++) final class Semantic2Visitor : Visitor assert(fd.semanticRun <= PASS.semantic2); fd.semanticRun = PASS.semantic2; + timeTraceBeginEvent(TimeTraceEventType.sema2); + scope(exit) timeTraceEndEvent(TimeTraceEventType.sema2, fd); + //printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr); // Only check valid functions which have a body to avoid errors @@ -896,3 +865,51 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor this.visit(crExp.value); } } + +/** + * Given a static assert with a failing condition, print an error + * Params: + * sa = Static assert with failing condition + * sc = scope for evaluating assert message and printing context + */ +void staticAssertFail(StaticAssert sa, Scope* sc) +{ + if (sa.msgs) + { + OutBuffer msgbuf; + for (size_t i = 0; i < sa.msgs.length; i++) + { + Expression e = (*sa.msgs)[i]; + sc = sc.startCTFE(); + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + e = ctfeInterpretForPragmaMsg(e); + if (e.op == EXP.error) + { + errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars()); + if (!global.gag) + fatal(); + return; + } + if (StringExp se = e.toStringExp()) + { + const slice = se.toUTF8(sc).peekString(); + // Hack to keep old formatting to avoid changing error messages everywhere + if (sa.msgs.length == 1) + msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr); + else + msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr); + } + else + msgbuf.printf("%s", e.toChars()); + } + error(sa.loc, "static assert: %s", msgbuf.extractChars()); + } + else + error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); + if (sc.tinst) + sc.tinst.printInstantiationTrace(); + if (!global.gag) + fatal(); +} diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 963fa9238a085c4c22ebc42baf23a6484b361d44..89c97cf6be4b02f7e8119f171169d4b200ac1a45 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -119,7 +119,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc.tinst = tempinst; sc.minst = tempinst.minst; - int needGagging = (tempinst.gagged && !global.gag); + bool needGagging = tempinst.gagged && !global.gag; uint olderrors = global.errors; int oldGaggedErrors = -1; // dead-store to prevent spurious warning /* If this is a gagged instantiation, gag errors. @@ -221,6 +221,11 @@ private extern(C++) final class Semantic3Visitor : Visitor override void visit(FuncDeclaration funcdecl) { //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl.kind(), funcdecl.toChars(), sc); + import dmd.timetrace; + import dmd.root.string : toDString; + timeTraceBeginEvent(TimeTraceEventType.sema3); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema3, funcdecl); + /* Determine if function should add `return 0;` */ bool addReturn0() @@ -229,7 +234,7 @@ private extern(C++) final class Semantic3Visitor : Visitor auto f = funcdecl.type.isTypeFunction(); // C11 5.1.2.2.3 - if (sc.flags & SCOPE.Cfile && funcdecl.isCMain() && f.next.ty == Tint32) + if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32) return true; return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain()); @@ -290,7 +295,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - //printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract)); + //printf(" sc.incontract = %d\n", sc.contract); if (funcdecl.semanticRun >= PASS.semantic3) return; funcdecl.semanticRun = PASS.semantic3; @@ -342,7 +347,10 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.explicitVisibility = 0; sc2.aligndecl = null; if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure) - sc2.flags = sc.flags & ~SCOPE.contract; + { + sc2.copyFlagsFrom(sc); + sc2.contract = Contract.none; + } sc2.tf = null; sc2.os = null; sc2.inLoop = false; @@ -543,8 +551,7 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement fpreinv = null; if (funcdecl.addPreInvariant()) { - Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); - if (e) + if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis)) fpreinv = new ExpStatement(Loc.initial, e); } @@ -552,8 +559,7 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement fpostinv = null; if (funcdecl.addPostInvariant()) { - Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); - if (e) + if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis)) fpostinv = new ExpStatement(Loc.initial, e); } @@ -820,7 +826,7 @@ private extern(C++) final class Semantic3Visitor : Visitor else { const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0; - if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !(sc.flags & SCOPE.Cfile)) + if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !sc.inCfile) { if (!funcdecl.hasReturnExp) .error(funcdecl.loc, "%s `%s` has no `return` statement, but is expected to return a value of type `%s`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars()); @@ -996,7 +1002,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sym.parent = sc2.scopesym; sym.endlinnum = funcdecl.endloc.linnum; sc2 = sc2.push(sym); - sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require; + sc2.contract = Contract.require; // BUG: need to error if accessing out parameters // BUG: need to disallow returns @@ -1041,7 +1047,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } sc2 = scout; //push - sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure; + sc2.contract = Contract.ensure; // BUG: need to disallow returns and throws @@ -1212,8 +1218,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { /* Wrap the entire function body in a synchronized statement */ - ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration(); - if (cd) + if (ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration()) { if (target.libraryObjectMonitors(funcdecl, sbody)) { diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d index 57a6639575c9c5693aa26691c23cf69e4da1fec0..4bcfb18881f6dba321bae3735fe26d19781371de 100644 --- a/gcc/d/dmd/sideeffect.d +++ b/gcc/d/dmd/sideeffect.d @@ -408,7 +408,7 @@ Expression extractSideEffect(Scope* sc, const char[] name, * https://issues.dlang.org/show_bug.cgi?id=17145 */ if (!alwaysCopy && - ((sc.flags & SCOPE.ctfe) ? !hasSideEffect(e) : isTrivialExp(e))) + (sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e))) return e; auto vd = copyToTemp(0, name, e); diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index c5c8a12593653faef4833d642b4d26c167213ea7..3b96ecf7c0cd8d5b120ddea6f313fe3a6c964f80 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -82,13 +82,13 @@ version (DMDLIB) */ private Identifier fixupLabelName(Scope* sc, Identifier ident) { - uint flags = (sc.flags & SCOPE.contract); + Contract c = sc.contract; const id = ident.toString(); - if (flags && flags != SCOPE.invariant_ && + if (c != Contract.none && c != Contract.invariant_ && !(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__" { OutBuffer buf; - buf.writestring(flags == SCOPE.require ? "__in_" : "__out_"); + buf.writestring(c == Contract.require ? "__in_" : "__out_"); buf.writestring(ident.toString()); ident = Identifier.idPool(buf[]); @@ -123,7 +123,7 @@ private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) @safe */ private Expression checkAssignmentAsCondition(Expression e, Scope* sc) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return e; auto ec = lastComma(e); if (ec.op == EXP.assign) @@ -205,7 +205,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } if (checkMustUse(s.exp, sc)) s.exp = ErrorExp.get(); - if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp)) + if (!sc.inCfile && discardValue(s.exp)) s.exp = ErrorExp.get(); s.exp = s.exp.optimize(WANTvalue); @@ -1697,7 +1697,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (ifs.isIfCtfeBlock()) { Scope* scd2 = scd.push(); - scd2.flags |= SCOPE.ctfeBlock; + scd2.ctfeBlock = true; ifs.ifbody = ifs.ifbody.semanticNoScope(scd2); scd2.pop(); } @@ -1733,11 +1733,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // This feature allows a limited form of conditional compilation. if (cs.condition.include(sc)) { - DebugCondition dc = cs.condition.isDebugCondition(); - if (dc) + if (DebugCondition dc = cs.condition.isDebugCondition()) { sc = sc.push(); - sc.flags |= SCOPE.debug_; + sc.debug_ = true; cs.ifbody = cs.ifbody.statementSemantic(sc); sc.pop(); } @@ -1964,7 +1963,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe); if (!ss.hasDefault) { - if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !(sc.flags & SCOPE.Cfile)) + if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile) error(ss.loc, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`"); // Generate runtime error if the default is hit @@ -1972,7 +1971,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) CompoundStatement cs; Statement s; - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } @@ -2023,7 +2022,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) ss._body = cs; } - if (!(sc.flags & SCOPE.Cfile) && ss.checkLabel()) + if (!sc.inCfile && ss.checkLabel()) { sc.pop(); return setError(); @@ -2474,7 +2473,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Expression e0 = null; bool errors = false; - if (sc.flags & SCOPE.contract) + if (sc.contract) { error(rs.loc, "`return` statements cannot be in contracts"); errors = true; @@ -3331,7 +3330,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) /* If catch exception type is derived from Exception */ if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && - (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_)) + (!c.handler || !c.handler.comeFrom()) && !sc.debug_) { // Remove c from the array of catches tcs.catches.remove(i); @@ -3459,7 +3458,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (ds.statement) { sc = sc.push(); - sc.flags |= SCOPE.debug_; + sc.debug_ = true; ds.statement = ds.statement.statementSemantic(sc); sc.pop(); } @@ -3480,7 +3479,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) gs.tf = sc.tf; gs.os = sc.os; gs.lastVar = sc.lastVar; - gs.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0; + gs.inCtfeBlock = sc.ctfeBlock; if (!gs.label.statement && sc.fes) { @@ -3504,7 +3503,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) fd.gotos = new GotoStatements(); fd.gotos.push(gs); } - else if (!(sc.flags & SCOPE.Cfile) && gs.checkLabel()) + else if (!sc.inCfile && gs.checkLabel()) return setError(); result = gs; @@ -3520,7 +3519,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) ls.tf = sc.tf; ls.os = sc.os; ls.lastVar = sc.lastVar; - ls.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0; + ls.inCtfeBlock = sc.ctfeBlock; LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc); if (ls2.statement && !ls2.duplicated) diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d index d26e35d814a48e92d15db7abc620490c9a78790d..afd0add448892e3d6e494f5bd821ddfbb1b66b4f 100644 --- a/gcc/d/dmd/templatesem.d +++ b/gcc/d/dmd/templatesem.d @@ -154,7 +154,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) /* Calculate TemplateParameter.dependent */ - TemplateParameters tparams = TemplateParameters(1); + auto tparams = TemplateParameters(1); for (size_t i = 0; i < tempdecl.parameters.length; i++) { TemplateParameter tp = (*tempdecl.parameters)[i]; @@ -457,7 +457,7 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, // (previously, this was immediately before calling evalStaticCondition), so the // semantic pass knows not to issue deprecation warnings for these throw-away decls. // https://issues.dlang.org/show_bug.cgi?id=21831 - scx.flags |= SCOPE.constraint; + scx.inTemplateConstraint = true; assert(!ti.symtab); if (fd) @@ -1337,10 +1337,10 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat * We also save/restore sc.func.flags to avoid messing up * attribute inference in the evaluation. */ - const oldflags = sc.func ? sc.func.flags : 0; + const oldflags = sc.func ? sc.func.saveFlags : 0; auto e = resolveAliasThis(sc, farg, true); if (sc.func) - sc.func.flags = oldflags; + sc.func.restoreFlags(oldflags); if (e) { farg = e; diff --git a/gcc/d/dmd/timetrace.d b/gcc/d/dmd/timetrace.d new file mode 100644 index 0000000000000000000000000000000000000000..7d1fd7342c2193b1ffed68d6b41a35b57730bcdf --- /dev/null +++ b/gcc/d/dmd/timetrace.d @@ -0,0 +1,82 @@ +/** +Compilation time tracing, -ftime-trace. + +The time trace profile is output in the Chrome Trace Event Format, described +here: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview + +This file is originally from LDC (the LLVM D compiler). + +Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved +Authors: Johan Engelen, Max Haughton, Dennis Korpel +License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) +Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/timetrace.d, common/_timetrace.d) +Documentation: https://dlang.org/phobos/dmd_common_timetrace.html +Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/timetrace.d +*/ +module dmd.timetrace; + +import dmd.dsymbol; +import dmd.expression; + +/** + * Start a new time trace event + * + * Details of the event will be passed as delegates to `timeTraceEndEvent` so + * they're only generated when the event is actually written. + * + * Params: + * eventType = what compilation stage the event belongs to + * (redundant with the eventType of `timeTraceEndEvent` but used by GDC) + */ +extern (C++) +void timeTraceBeginEvent(TimeTraceEventType eventType) +{ +} + +/** + * End a time tracing event, optionally updating the event name and details + * with a delegate. Delegates are used to prevent spending time on string + * generation when an event is too small to be generated anyway. + * + * Params: + * eventType = what compilation stage the event belongs to + * sym = Dsymbol which was analyzed, used to generate 'name' and 'detail' + * e = Expression which was analyzed, used to generate 'name' and 'detail' + * detail = custom lazy string for 'detail' of event + */ +extern (C++) +void timeTraceEndEvent(TimeTraceEventType eventType) +{ +} + +/// ditto +void timeTraceEndEvent(TimeTraceEventType eventType, Dsymbol sym, scope const(char)[] delegate() detail = null) +{ + return timeTraceEndEvent(eventType); +} + +/// ditto +extern (C++) +void timeTraceEndEvent(TimeTraceEventType eventType, Expression e) +{ + return timeTraceEndEvent(eventType); +} + +/// Identifies which compilation stage the event is associated to +enum TimeTraceEventType +{ + generic, + parseGeneral, + parse, + semaGeneral, + sema1Import, + sema1Module, + sema2, + sema3, + ctfe, + ctfeCall, + codegenGlobal, + codegenModule, + codegenFunction, + link, +} diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 5ec38449123cf973ffa4c2ce1063ff0ecbec7db0..c8608fafc7980fa865bb64e081d30f72c8526ba7 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -736,7 +736,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); Scope* sc2 = sc.push(); - sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; + sc2.copyFlagsFrom(sc); + sc2.noAccessCheck = true; + sc2.ignoresymbolvisibility = true; bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) @@ -772,7 +774,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); Scope* sc2 = sc.push(); - sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; + sc2.copyFlagsFrom(sc); + sc2.noAccessCheck = true; + sc2.ignoresymbolvisibility = true; bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) @@ -1003,7 +1007,8 @@ Expression semanticTraits(TraitsExp e, Scope* sc) doSemantic: // ignore symbol visibility and disable access checks for these traits Scope* scx = sc.push(); - scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; + scx.ignoresymbolvisibility = true; + scx.noAccessCheck = true; scope (exit) scx.pop(); if (e.ident == Id.hasMember) @@ -1737,7 +1742,11 @@ Expression semanticTraits(TraitsExp e, Scope* sc) Scope* sc2 = sc.push(); sc2.tinst = null; sc2.minst = null; // this is why code for these are not emitted to object file - sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst; + sc2.copyFlagsFrom(sc); + sc2.ctfe = false; + sc2.condition = false; + sc2.traitsCompiles = true; + sc2.fullinst = true; bool err = false; diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 33d825a2b848b933ed44fe936123e86d364e7ea9..e4d7a95f8880002a7702443aa9a612c8c4e00322 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -235,7 +235,7 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb Dsymbol sm = s.searchX(loc, sc, id, flags); if (sm) { - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, sm)) { .error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); sm = null; @@ -620,7 +620,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) return false; // Don't complain if we're inside a template constraint // https://issues.dlang.org/show_bug.cgi?id=21831 - if (sc.flags & SCOPE.constraint) + if (sc.inTemplateConstraint) return false; Type t = type.baseElemOf(); @@ -633,7 +633,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) if (t.isimaginary() || t.iscomplex()) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return true; // complex/imaginary not deprecated in C code Type rt; switch (t.ty) @@ -1012,7 +1012,7 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, else { import dmd.dcast : cimplicitConvTo; - m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); + m = (sc && sc.inCfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); } } @@ -1445,6 +1445,163 @@ uinteger_t size(Type t, const ref Loc loc) } } +/******************************* + * Determine if converting 'this' to 'to' is an identity operation, + * a conversion to const operation, or the types aren't the same. + * Returns: + * MATCH.exact 'this' == 'to' + * MATCH.constant 'to' is const + * MATCH.nomatch conversion to mutable or invariant + */ +MATCH constConv(Type from, Type to) +{ + MATCH visitType(Type from) + { + //printf("Type::constConv(this = %s, to = %s)\n", from.toChars(), to.toChars()); + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitNext(TypeNext from) + { + //printf("TypeNext::constConv from = %s, to = %s\n", from.toChars(), to.toChars()); + if (from.equals(to)) + return MATCH.exact; + + if (!(from.ty == to.ty && MODimplicitConv(from.mod, to.mod))) + return MATCH.nomatch; + + Type tn = to.nextOf(); + if (!(tn && from.next.ty == tn.ty)) + return MATCH.nomatch; + + MATCH m; + if (to.isConst()) // whole tail const conversion + { + // Recursive shared level check + m = from.next.constConv(tn); + if (m == MATCH.exact) + m = MATCH.constant; + } + else + { + //printf("\tnext => %s, to.next => %s\n", from.next.toChars(), tn.toChars()); + m = from.next.equals(tn) ? MATCH.constant : MATCH.nomatch; + } + return m; + } + + MATCH visitSArray(TypeSArray from) + { + if (auto tsa = to.isTypeSArray()) + { + if (!from.dim.equals(tsa.dim)) + return MATCH.nomatch; + } + return visitNext(from); + } + + MATCH visitAArray(TypeAArray from) + { + if (auto taa = to.isTypeAArray()) + { + MATCH mindex = from.index.constConv(taa.index); + MATCH mkey = from.next.constConv(taa.next); + // Pick the worst match + return mkey < mindex ? mkey : mindex; + } + return visitType(from); + } + + MATCH visitPointer(TypePointer from) + { + if (from.next.ty == Tfunction) + { + if (to.nextOf() && from.next.equals((cast(TypeNext)to).next)) + return visitType(from); + else + return MATCH.nomatch; + } + return visitNext(from); + } + + MATCH visitFunction(TypeFunction from) + { + // Attributes need to match exactly, otherwise it's an implicit conversion + if (from.ty != to.ty || !from.attributesEqual(cast(TypeFunction) to)) + return MATCH.nomatch; + + return visitNext(from); + } + + MATCH visitStruct(TypeStruct from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeStruct)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitEnum(TypeEnum from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeEnum)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitClass(TypeClass from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeClass)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + + /* Conversion derived to const(base) + */ + int offset = 0; + if (to.isBaseOf(from, &offset) && offset == 0 && MODimplicitConv(from.mod, to.mod)) + { + // Disallow: + // derived to base + // inout(derived) to inout(base) + if (!to.isMutable() && !to.isWild()) + return MATCH.convert; + } + + return MATCH.nomatch; + } + + MATCH visitNoreturn(TypeNoreturn from) + { + // Either another noreturn or conversion to any type + return from.implicitConvTo(to); + } + + switch(from.ty) + { + default: return visitType(from); + case Tsarray: return visitSArray(from.isTypeSArray()); + case Taarray: return visitAArray(from.isTypeAArray()); + case Treference: + case Tdelegate: + case Tslice: + case Tarray: return visitNext(cast(TypeNext)from); + case Tpointer: return visitPointer(from.isTypePointer()); + case Tfunction: return visitFunction(from.isTypeFunction()); + case Tstruct: return visitStruct(from.isTypeStruct()); + case Tenum: return visitEnum(from.isTypeEnum()); + case Tclass: return visitClass(from.isTypeClass()); + case Tnoreturn: return visitNoreturn(from.isTypeNoreturn()); + } +} + + /****************************************** * Perform semantic analysis on a type. * Params: @@ -1479,7 +1636,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) Type visitComplex(TypeBasic t) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return visitType(t); auto tc = getComplexLibraryType(loc, sc, t.ty); @@ -1653,7 +1810,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (tbn.isscope()) + if (tbn.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", tbn.toChars()); return error(); @@ -1687,7 +1844,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (tn.isscope()) + if (tn.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", tn.toChars()); return error(); @@ -1893,7 +2050,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (mtype.next.isscope()) + if (mtype.next.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", mtype.next.toChars()); return error(); @@ -2059,7 +2216,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) tf.next = tf.next.typeSemantic(loc, sc); sc = sc.pop(); errors |= tf.checkRetType(loc); - if (tf.next.isscope() && !tf.isctor) + if (tf.next.isScopeClass() && !tf.isctor) { .error(loc, "functions cannot return `scope %s`", tf.next.toChars()); errors = true; @@ -2457,7 +2614,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 && - !(sc.flags & SCOPE.Cfile)) + !sc.inCfile) { .error(loc, "variadic functions with non-D linkage must have at least one parameter"); errors = true; @@ -3209,7 +3366,28 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden else { if (src) - error(loc, "no property `%s` for `%s` of type `%s`", ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + { + error(loc, "no property `%s` for `%s` of type `%s`", + ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + auto s2 = scope_.search_correct(ident); + // UFCS + if (s2 && s2.isFuncDeclaration) + errorSupplemental(loc, "did you mean %s `%s`?", + s2.kind(), s2.toChars()); + else if (src.type.ty == Tpointer) + { + // structPtr.field + auto tn = (cast(TypeNext) src.type).nextOf(); + if (auto as = tn.isAggregate()) + { + if (auto s3 = as.search_correct(ident)) + { + errorSupplemental(loc, "did you mean %s `%s`?", + s3.kind(), s3.toChars()); + } + } + } + } else error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); @@ -3897,7 +4075,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type // compile time sequences are valid types !mt.exp.type.isTypeTuple()) { - if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof + if (!sc.inCfile && // in (extended) C typeof may be used on types as with sizeof mt.exp.checkType()) goto Lerr; @@ -4786,7 +4964,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag assert(e.op != EXP.dot); // https://issues.dlang.org/show_bug.cgi?id=14010 - if (!(sc.flags & SCOPE.Cfile) && ident == Id._mangleof) + if (!sc.inCfile && ident == Id._mangleof) { return mt.getProperty(sc, e.loc, ident, flag & 1); } @@ -4798,7 +4976,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Create a TupleExp out of the fields of the struct e: * (e.field0, e.field1, e.field2, ...) */ - e = e.expressionSemantic(sc); // do this before turning on noaccesscheck + e = e.expressionSemantic(sc); // do this before turning on noAccessCheck if (!mt.sym.determineFields()) { @@ -4828,20 +5006,20 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; e = e.expressionSemantic(sc2); sc2.pop(); return e; } - immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; + immutable flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: if (!s) { return noMember(mt, sc, e, ident, flag); } - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s)) { return noMember(mt, sc, e, ident, flag); } @@ -5078,7 +5256,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Create a TupleExp */ - e = e.expressionSemantic(sc); // do this before turning on noaccesscheck + e = e.expressionSemantic(sc); // do this before turning on noAccessCheck mt.sym.size(e.loc); // do semantic of type @@ -5108,13 +5286,13 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; e = e.expressionSemantic(sc2); sc2.pop(); return e; } - SearchOptFlags flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; + SearchOptFlags flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: @@ -5267,7 +5445,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag return noMember(mt, sc, e, ident, flag & 1); } - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s)) { return noMember(mt, sc, e, ident, flag); } @@ -5542,6 +5720,74 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } } +// if initializer is 0 +bool isZeroInit(Type t, const ref Loc loc) +{ + bool visitType(Type _) + { + return false; // assume not + } + + bool visitBasic(TypeBasic t) + { + switch (t.ty) + { + case Tchar: + case Twchar: + case Tdchar: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + return false; // no + default: + return true; // yes + } + } + + bool visitVector(TypeVector t) + { + return t.basetype.isZeroInit(loc); + } + + bool visitSArray(TypeSArray t) + { + return t.next.isZeroInit(loc); + } + + bool visitStruct(TypeStruct t) + { + // Determine zeroInit here, as this can be called before semantic2 + t.sym.determineSize(t.sym.loc); + return t.sym.zeroInit; + } + + bool visitEnum(TypeEnum t) + { + return t.sym.getDefaultValue(loc).toBool().hasValue(false); + } + + switch(t.ty) + { + default: return t.isTypeBasic() ? visitBasic(cast(TypeBasic)t) : visitType(t); + case Tvector: return visitVector(t.isTypeVector()); + case Tsarray: return visitSArray(t.isTypeSArray()); + case Taarray: + case Tarray: + case Treference: + case Tdelegate: + case Tclass: + case Tpointer: return true; + case Tstruct: return visitStruct(t.isTypeStruct()); + case Tenum: return visitEnum(t.isTypeEnum()); + } +} + /************************ * Get the default initialization expression for a type. @@ -7136,6 +7382,100 @@ bool isRecursiveAliasThis(ref Type att, Type t) return false; } +MATCH implicitConvToWithoutAliasThis(TypeStruct from, Type to) +{ + //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars()); + + auto tos = to.isTypeStruct(); + if (!(tos && from.sym == tos.sym)) + return MATCH.nomatch; + + if (from.mod == to.mod) + return MATCH.exact; + + if (MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + + /* Check all the fields. If they can all be converted, + * allow the conversion. + */ + MATCH m = MATCH.constant; + uint offset = ~0; // must never match a field offset + foreach (v; from.sym.fields[]) + { + /* Why are we only looking at the first member of a union? + * The check should check for overlap of v with the previous field, + * not just starting at the same point + */ + if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field + continue; // ignore + + Type tvf = v.type.addMod(from.mod); // from type + Type tvt = v.type.addMod(to.mod); // to type + + // field match + MATCH mf = tvf.implicitConvTo(tvt); + //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf); + + if (mf == MATCH.nomatch) + return MATCH.nomatch; + if (mf < m) // if field match is worse + m = mf; + offset = v.offset; + } + return m; +} + +MATCH implicitConvToWithoutAliasThis(TypeClass from, Type to) +{ + ClassDeclaration cdto = to.isClassHandle(); + MATCH m = constConv(from, to); + if (m > MATCH.nomatch) + return m; + + if (cdto && cdto.isBaseOf(from.sym, null) && MODimplicitConv(from.mod, to.mod)) + { + //printf("'to' is base\n"); + return MATCH.convert; + } + return MATCH.nomatch; +} + +MATCH implicitConvToThroughAliasThis(TypeClass from, Type to) +{ + MATCH m; + if (from.sym.aliasthis && !(from.att & AliasThisRec.tracing)) + { + if (auto ato = aliasthisOf(from)) + { + from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing); + m = ato.implicitConvTo(to); + from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing); + } + } + return m; +} + +MATCH implicitConvToThroughAliasThis(TypeStruct from, Type to) +{ + auto tos = to.isTypeStruct(); + if (!(tos && from.sym == tos.sym) && + from.sym.aliasthis && + !(from.att & AliasThisRec.tracing)) + { + if (auto ato = aliasthisOf(from)) + { + from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing); + MATCH m = ato.implicitConvTo(to); + from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing); + return m; + } + } + return MATCH.nomatch; +} + + + /******************************* Private *****************************************/ private: @@ -7257,8 +7597,7 @@ Type stripDefaultArgs(Type t) { foreach (i, p; *parameters) { - Parameter ps = stripParameter(p); - if (ps) + if (Parameter ps = stripParameter(p)) { // Replace params with a copy we can modify Parameters* nparams = new Parameters(parameters.length); diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d index a3198327fb2eeac414d871906db739497ce94751..5d7154a39d99aa01b878c0e4e5326b8e079b020e 100644 --- a/gcc/d/dmd/typinf.d +++ b/gcc/d/dmd/typinf.d @@ -42,7 +42,7 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) // Even when compiling without `useTypeInfo` (e.g. -betterC) we should // still be able to evaluate `TypeInfo` at compile-time, just not at runtime. // https://issues.dlang.org/show_bug.cgi?id=18472 - if (!sc || !(sc.flags & SCOPE.ctfe)) + if (!sc || !sc.ctfe) { if (!global.params.useTypeInfo) { diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index 9228ba69b1c5c85ac0a1245a948e086832419f48..cde029d58674d3962b3f56272ee1f2702f90e235 100644 --- a/gcc/d/dmd/utils.d +++ b/gcc/d/dmd/utils.d @@ -124,7 +124,7 @@ bool ensurePathToNameExists(Loc loc, const(char)[] name) * buf = Buffer to write the escaped path to * fname = Path to escape */ -void escapePath(OutBuffer* buf, const(char)* fname) +void escapePath(OutBuffer* buf, const(char)* fname) pure { while (1) { @@ -145,78 +145,6 @@ void escapePath(OutBuffer* buf, const(char)* fname) } } -/** - * Takes a path, and make it compatible with GNU Makefile format. - * - * GNU make uses a weird quoting scheme for white space. - * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space; - * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name; - * and backslashes in other contexts should not be doubled. - * - * Params: - * buf = Buffer to write the escaped path to - * fname = Path to escape - */ -void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname) -{ - uint slashes; - - while (*fname) - { - switch (*fname) - { - case '\\': - slashes++; - break; - case '$': - buf.writeByte('$'); - goto default; - case ' ': - case '\t': - while (slashes--) - buf.writeByte('\\'); - goto case; - case '#': - buf.writeByte('\\'); - goto default; - case ':': - // ':' not escaped on Windows because it can - // create problems with absolute paths (e.g. C:\Project) - version (Windows) {} - else - { - buf.writeByte('\\'); - } - goto default; - default: - slashes = 0; - break; - } - - buf.writeByte(*fname); - fname++; - } -} - -/// -unittest -{ - version (Windows) - { - enum input = `C:\My Project\file#4$.ext`; - enum expected = `C:\My\ Project\file\#4$$.ext`; - } - else - { - enum input = `/foo\bar/weird$.:name#\ with spaces.ext`; - enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`; - } - - OutBuffer buf; - buf.writeEscapedMakePath(input); - assert(buf[] == expected); -} - /** * Convert string to integer. * diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 46d30514287396aea3c5a36517705f828ad6e102..8a242b887807a62e47d8555bb96751b0ecfc9563 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -2180,7 +2180,7 @@ public: tree type = build_ctype (e->type); tree length = size_int (sd->dsym->structsize); tree ptr = (sd->dsym->isStructDeclaration () - && sd->dsym->type->isZeroInit (e->loc)) + && dmd::isZeroInit (sd->dsym->type, e->loc)) ? null_pointer_node : build_address (result); this->result_ = d_array_value (type, length, ptr); diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index 982d7cc95dd52104d26b9c1f165817274572d20c..c0a496af2fe5bf6882d90bf400ba8714ad15dfdd 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -662,7 +662,7 @@ public: this->layout_string (ed->toPrettyChars ()); /* Default initializer for enum. */ - if (ed->members && !d->tinfo->isZeroInit ()) + if (ed->members && !dmd::isZeroInit (d->tinfo)) { tree length = size_int (dmd::size (ed->type)); tree ptr = build_address (enum_initializer_decl (ed)); @@ -1415,7 +1415,7 @@ check_typeinfo_type (const Loc &loc, Scope *sc, Expression *expr) { /* Even when compiling without RTTI we should still be able to evaluate TypeInfo at compile-time, just not at run-time. */ - if (!sc || !(sc->flags & unsigned(SCOPE::ctfe))) + if (!sc || !sc->ctfe ()) { static int warned = 0; diff --git a/gcc/testsuite/gdc.test/compilable/header18365.d b/gcc/testsuite/gdc.test/compilable/header18365.d index 7e51fb26cc770f0357e2cf76445aec4fd1c874f3..8846c86c445fe11ea4f979d7360136f13776c717 100644 --- a/gcc/testsuite/gdc.test/compilable/header18365.d +++ b/gcc/testsuite/gdc.test/compilable/header18365.d @@ -13,7 +13,7 @@ struct FullCaseEntry ubyte n; ubyte size; ubyte entry_len; - auto const pure nothrow @nogc @property @trusted value() return + auto pure nothrow @nogc @property @trusted value() const return { return seq[0..entry_len]; } diff --git a/gcc/testsuite/gdc.test/compilable/scope.d b/gcc/testsuite/gdc.test/compilable/scope.d index 44f54f7468ab394a511120e846051fb1a4eb54d6..06549941d255b1103b73910b49f9ee6e5ba20738 100644 --- a/gcc/testsuite/gdc.test/compilable/scope.d +++ b/gcc/testsuite/gdc.test/compilable/scope.d @@ -262,3 +262,116 @@ struct S23669 a.length += 1; } } + +/************************************/ + +// Calling `hasPointers` in escape.d at some point caused +// a "circular `typeof` definition" error in std.typecons +// https://github.com/dlang/dmd/pull/16719 +struct Unique +{ + int* p; + alias ValueType = typeof({ return p; } ()); + static if (is(ValueType == int*)) {} +} + +// Without handling tuple expansion in escape.d, this error occurs: +// Error: returning `(ref Tuple!(int, int) __tup2 = this.tuple.get();) , &__tup2.__expand_field_0` escapes a reference to local variable `__tup2` +struct Tuple(Types...) +{ + Types expand; + ref Tuple!Types get() { return this; } +} + +struct S2 +{ + Tuple!(int, int) tuple; + int* f() return { return &tuple.get().expand[0]; } +} + +/************************************/ + +// https://issues.dlang.org/show_bug.cgi?id=23300 + +auto array(Range)(Range r) +{ + int[] result; + foreach (e; r) + result ~= e; + return result; +} + +struct Range +{ + int* ptr; + int front = 3; + enum empty = true; + void popFront() @safe scope {} +} + +auto f() @safe +{ + scope Range r; + return r.array; +} + +/************************************/ + +// Test that there's no endless loop forwarding `__appendtmp1 => __appendtmp1.this(null)` +struct SD +{ + int* p; + this(int*) return scope {} +} + +auto fsd() @safe +{ + SD[] a; + a ~= SD(null); +} + +/************************************/ + +// Test that `return scope` inference from assignment doesn't force the function to be `scope` +struct Scape +{ + int* p; + + this()(int* input) + { + p = input; // return scope inferred here + notScope(); // not-scope inferred here + } + + void notScope() @safe + { + static int* g; + g = p; + } +} + +@safe testScape() +{ + Scape(null); +} + +/************************************/ + +// Test `scope` inference in presence of nested function returning `this`: +// `save()` can still be called on a `scope Result` +struct Result +{ + int* source; + auto save() + { + int* saveI() { return this.source; } + auto saveResult = Result(saveI()); + } +} + +@safe escapeNested() +{ + int s; + auto r = Result(&s); + r.save(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d index 13974aa0e9a6fa8986fb47ce51df8482b2f0b852..02fc7db06183e8debd7b4c22cef4d806cdc442e4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d +++ b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d @@ -4,7 +4,7 @@ TEST_OUTPUT: fail_compilation/bug9631.d(20): Error: cannot implicitly convert expression `F()` of type `bug9631.T1!().F` to `bug9631.T2!().F` --- */ -// DISABLED: win32 + template T1() { struct F { } diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9679.d b/gcc/testsuite/gdc.test/fail_compilation/diag9679.d index 85923b7189e11db6e053f95dcd34f407de8eddb2..066e2166d315e8a46390b67d7c6d504cece2a748 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag9679.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag9679.d @@ -1,13 +1,73 @@ -/* +/* REQUIRED_ARGS: -verrors=0 TEST_OUTPUT: --- -fail_compilation/diag9679.d(11): Error: variable `diag9679.main.n` - only parameters, functions and `foreach` declarations can be `ref` -fail_compilation/diag9679.d(12): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`? +fail_compilation/diag9679.d(15): Error: rvalue `1` cannot be assigned to `ref n` +fail_compilation/diag9679.d(16): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`? +fail_compilation/diag9679.d(17): Error: variable `diag9679.main.S.a` - field declarations cannot be `ref` +fail_compilation/diag9679.d(24): Error: returning `r` escapes a reference to local variable `i` --- */ + + void main() { if (ref n = 1) {} if (auto int n = 1) {} + struct S { ref int a; } +} + +ref int test2() +{ + int i; + ref r = i; + return r; +} + +ref int test3() +{ + extern int i; + ref r = i; + return r; +} + +struct S { int a; } + +void test4() +{ + S s; + ref int r1 = s.a; + r1 = 3; + __gshared S t2; + ref int r2 = t2.a; + static S t3; + ref int r3 = t3.a; + extern S t4; + ref int r4 = t4.a; +} + +/* TEST_OUTPUT: +--- +fail_compilation/diag9679.d(60): Error: variable `diag9679.test5.r5` - initializer is required for `ref` variable +fail_compilation/diag9679.d(60): Error: rvalue `0` cannot be assigned to `ref r5` +fail_compilation/diag9679.d(65): Error: rvalue `4` cannot be assigned to `ref x` +fail_compilation/diag9679.d(66): Error: returning `x` escapes a reference to local variable `x` +fail_compilation/diag9679.d(71): Error: type `immutable(int)` cannot be assigned to `ref int x` +--- +*/ +void test5() +{ + ref int r5; +} + +ref int test6() +{ + ref int x = 4; + return x; +} + +void test7(immutable int y) +{ + ref int x = y; + x = 5; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13902.d b/gcc/testsuite/gdc.test/fail_compilation/fail13902.d index 47cb65cde24fc8dee64f1ffc7568c0b67ad9c009..9c82b2c28153ac4ae1ef1b7cab1963ba8ae5a2ff 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13902.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13902.d @@ -323,9 +323,9 @@ int[] testSlice2() { int[3] sa; int n; return sa[n..2][1..2]; } /* TEST_OUTPUT: --- -fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to parameter `vda` +fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to variadic parameter `vda` +fail_compilation/fail13902.d(325): Error: returning `vda[]` escapes a reference to variadic parameter `vda` --- - */ ref int testDynamicArrayVariadic1(int[] vda...) { return vda[0]; } @safe int[] testDynamicArrayVariadic2(int[] vda...) { return vda[]; } @@ -335,9 +335,28 @@ int[3] testDynamicArrayVariadic3(int[] vda...) { return vda[0..3]; } // no er TEST_OUTPUT: --- fail_compilation/fail13902.d(335): Error: returning `vsa[0]` escapes a reference to parameter `vsa` -fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to variadic parameter `vsa` +fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to parameter `vsa` --- */ ref int testStaticArrayVariadic1(int[3] vsa...) { return vsa[0]; } int[] testStaticArrayVariadic2(int[3] vsa...) { return vsa[]; } int[3] testStaticArrayVariadic3(int[3] vsa...) { return vsa[0..3]; } // no error + +/* +TEST_OUTPUT: +--- +fail_compilation/fail13902.d(355): Error: returning `match(st)` escapes a reference to local variable `st` +--- +*/ + +// This was reduced from a `static assert(!__traits(compiles, {...}))` test in `std.sumtype` +// which was asserting that matchers couldn't escape sumtype members. +// This should give an error even without `@safe` or `-preview=dip1000` + +int* match(return ref int i) { return &i; } + +int* escape() +{ + int st; + return match(st); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19948.d b/gcc/testsuite/gdc.test/fail_compilation/fail19948.d index 9c23b78df926ecc59d5d0a9eca5ea52da2c3b125..09737e5b25f46893242fd6e82cd761e6792e1e6e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail19948.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19948.d @@ -1,5 +1,5 @@ // https://issues.dlang.org/show_bug.cgi?id=19948 -// DISABLED: win32 + /* TEST_OUTPUT: --- @@ -8,7 +8,7 @@ fail_compilation/fail19948.d(16): cannot pass argument `X()` of type `fai fail_compilation/fail19948.d(19): `fail19948.func(const(X))` declared here --- */ -// DISABLED: win32 + struct X {} void main() { diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20183.d b/gcc/testsuite/gdc.test/fail_compilation/fail20183.d index f04db02493263e03085450ba66730c409afc5b32..9d99212adac7a3418eea6b0f17bffdcc533c91dc 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail20183.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail20183.d @@ -3,15 +3,15 @@ TEST_OUTPUT: --- fail_compilation/fail20183.d(1016): Error: function `addr` is not callable using argument types `(int)` fail_compilation/fail20183.d(1016): cannot pass rvalue argument `S(0).i` of type `int` to parameter `return ref int b` -fail_compilation/fail20183.d(1005): `fail20183.addr(return ref int b)` declared here -fail_compilation/fail20183.d(1017): Error: address of struct temporary returned by `s()` assigned to longer lived variable `q` +fail_compilation/fail20183.d(1004): `fail20183.addr(return ref int b)` declared here +fail_compilation/fail20183.d(1017): Error: address of expression temporary returned by `s()` assigned to `q` with longer lifetime +fail_compilation/fail20183.d(1018): Error: address of struct literal `S(0)` assigned to `r` with longer lifetime --- */ #line 1000 // https://issues.dlang.org/show_bug.cgi?id=20183 - @safe: int* addr(return ref int b) { return &b; } @@ -19,20 +19,22 @@ int* addr(return ref int b) { return &b; } struct S { int i; + S* addrOf() return => &this; } S s() { return S(); } void test() { - int* p = addr(S().i); // struct literal - int* q = addr(s().i); // struct temporary + scope int* p = addr(S().i); // struct literal + scope int* q = addr(s().i); // struct temporary + scope S* r = S().addrOf(); // struct literal } /* TEST_OUTPUT: --- -fail_compilation/fail20183.d(1107): Error: address of struct temporary returned by `s()` assigned to longer lived variable `this.ptr` +fail_compilation/fail20183.d(1107): Error: address of expression temporary returned by `s()` assigned to `this.ptr` with longer lifetime --- */ #line 1100 diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail347.d b/gcc/testsuite/gdc.test/fail_compilation/fail347.d index 8d061f96fdb132c4d99bc881b8fd5ade4390d548..e495ba257e358a1b17898232a5338160e12327af 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail347.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail347.d @@ -2,9 +2,13 @@ EXTRA_FILES: imports/fail347a.d TEST_OUTPUT: --- -fail_compilation/fail347.d(22): Error: undefined identifier `bbr`, did you mean variable `bar`? -fail_compilation/fail347.d(23): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`? -fail_compilation/fail347.d(24): Error: undefined identifier `strlenx`, did you mean function `strlen`? +fail_compilation/fail347.d(26): Error: undefined identifier `bbr`, did you mean variable `bar`? +fail_compilation/fail347.d(27): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`? +fail_compilation/fail347.d(29): Error: no property `fool` for `sp` of type `fail347.S*` +fail_compilation/fail347.d(29): did you mean variable `foo`? +fail_compilation/fail347.d(30): Error: undefined identifier `strlenx`, did you mean function `strlen`? +fail_compilation/fail347.d(31): Error: no property `strlenx` for `"hello"` of type `string` +fail_compilation/fail347.d(31): did you mean function `strlen`? --- */ @@ -21,5 +25,8 @@ void main() S bar; bbr.foo = 3; bar.ofo = 4; + auto sp = &bar; + sp.fool = 5; auto s = strlenx("hello"); + auto q = "hello".strlenx(); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope.d b/gcc/testsuite/gdc.test/fail_compilation/retscope.d index ce983c01fc6f6cd26146ec8787da4d0896b8477a..919d9401cd5f960d1b72ef579edd1f9c2f665dff 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope.d @@ -7,7 +7,7 @@ fail_compilation/retscope.d(32): Error: returning `b ? nested1(& i) : nested2(& fail_compilation/retscope.d(45): Error: scope variable `p` assigned to global variable `q` fail_compilation/retscope.d(47): Error: address of variable `i` assigned to `q` with longer lifetime fail_compilation/retscope.d(48): Error: scope variable `a` assigned to global variable `b` -fail_compilation/retscope.d(49): Error: address of struct temporary returned by `(*fp2)()` assigned to longer lived variable `q` +fail_compilation/retscope.d(49): Error: address of expression temporary returned by `(*fp2)()` assigned to `q` with longer lifetime --- */ @@ -299,15 +299,31 @@ struct S7 /***************************************************/ +/* +TEST_OUTPUT: +--- +fail_compilation/retscope.d(315): Error: scope parameter `p` may not be returned +fail_compilation/retscope.d(316): Error: returning `p[]` escapes a reference to parameter `p` +fail_compilation/retscope.d(319): Error: scope parameter `p` may not be returned +--- +*/ + int[3] escape8(scope int[] p) @safe { return p[0 .. 3]; } // should not error char*[3] escape9(scope char*[] p) @safe { return p[0 .. 3]; } +// https://issues.dlang.org/show_bug.cgi?id=24663 + int*[3] escape8b(scope int*[3] p) @safe { return p[]; } +ref int*[3] escape9b( int*[3] p) @safe { return p[]; } + +ref int[3] asStatic(return scope int[] p) @safe { return p[0 .. 3]; } +ref int[3] asStatic2( scope int[] p) @safe { return p[0 .. 3]; } + /***************************************************/ /* TEST_OUTPUT: --- -fail_compilation/retscope.d(319): Error: reference to local variable `i` assigned to non-scope `f` +fail_compilation/retscope.d(335): Error: reference to local variable `i` assigned to non-scope `f` --- */ @@ -331,7 +347,7 @@ int* bar10( scope int** ptr ) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(342): Error: cannot take address of `scope` variable `aa` since `scope` applies to first indirection only +fail_compilation/retscope.d(358): Error: cannot take address of `scope` variable `aa` since `scope` applies to first indirection only --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope3.d b/gcc/testsuite/gdc.test/fail_compilation/retscope3.d index 951ad599d554869e80777eb802dabd8b3d4e05f0..0774d0d469aa369ca08d2a9e623648c140b0f460 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope3.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope3.d @@ -53,7 +53,7 @@ void bar4() /* TEST_OUTPUT: --- -fail_compilation/retscope3.d(4003): Error: copying `u[]` into allocated memory escapes a reference to variadic parameter `u` +fail_compilation/retscope3.d(4003): Error: copying `u[]` into allocated memory escapes a reference to parameter `u` fail_compilation/retscope3.d(4016): Error: storing reference to outer local variable `i` into allocated memory causes it to escape fail_compilation/retscope3.d(4025): Error: storing reference to stack allocated value returned by `makeSA()` into allocated memory causes it to escape --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d index ddeae81bc23efa7287d67e84ccf958227283bdeb..3271098367370adedfcfcc4b75ddb0f5f16c1eab 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d @@ -78,8 +78,11 @@ void foo() @safe fail_compilation/retscope6.d(8016): Error: address of variable `i` assigned to `p` with longer lifetime fail_compilation/retscope6.d(8031): Error: reference to local variable `i` assigned to non-scope parameter `p` calling `betty` fail_compilation/retscope6.d(8031): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `betty` -fail_compilation/retscope6.d(8021): which is assigned to non-scope parameter `p` +fail_compilation/retscope6.d(8023): which is not `scope` because of `p = q` +fail_compilation/retscope6.d(8048): Error: reference to local variable `i` assigned to non-scope parameter `p` calling `archie` +fail_compilation/retscope6.d(8039): which is not `scope` because of `r = p` fail_compilation/retscope6.d(8048): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `archie` +fail_compilation/retscope6.d(8038): which is not `scope` because of `p = q` --- */ @@ -109,8 +112,8 @@ void testfrankly() void betty()(int* p, int* q) { - p = q; - escape(p); + p = q; + escape(p); } void testbetty() @@ -124,9 +127,9 @@ void testbetty() void archie()(int* p, int* q, int* r) { - p = q; - r = p; - escape(q); + p = q; + r = p; + escape(q); } void testarchie() @@ -293,3 +296,26 @@ ref int escape23021() @safe } /******************************/ + +/* TEST_OUTPUT: +--- +fail_compilation/retscope6.d(14050): Error: scope variable `z` assigned to non-scope parameter `y` calling `f23294` +fail_compilation/retscope6.d(14044): which is not `scope` because of `x = y` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23294 + +@safe: +int g23294; + +auto f23294(int* x, int* y) +{ + x = y; + g23294++; // make sure it's not inferring scope from pure +} + +void escape23294(scope int* z) +{ + f23294(z, z); // passes +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/staticassert_sema1.d b/gcc/testsuite/gdc.test/fail_compilation/staticassert_sema1.d new file mode 100644 index 0000000000000000000000000000000000000000..e64c437de32b487be6438646dbad59cb0d90db26 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/staticassert_sema1.d @@ -0,0 +1,39 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/staticassert_sema1.d(17): Error: static assert: "unsupported OS" +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24645 +// Test that a static assert(0) is not drowned out by subsequent import errors. + +version(_NONEXISTENT_OS) +{ + +} +else +{ + static assert(0, msg); +} + +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; + +enum msg = "unsupported OS"; diff --git a/gcc/testsuite/gdc.test/fail_compilation/test14238.d b/gcc/testsuite/gdc.test/fail_compilation/test14238.d index a0e4d69b361fb4d2603db4663c15299b5866b698..e1cd39c595af30ff4eb7018093b994e0680ff07f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test14238.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test14238.d @@ -1,17 +1,19 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test14238.d(20): Error: scope parameter `fn` may not be returned -fail_compilation/test14238.d(28): Error: escaping reference to stack allocated value returned by `&baz` +fail_compilation/test14238.d(22): Error: scope parameter `fn` may not be returned +fail_compilation/test14238.d(25): Error: function `test14238.bar` is `@nogc` yet allocates closure for `bar()` with the GC +fail_compilation/test14238.d(27): function `test14238.bar.baz` closes over variable `x` +fail_compilation/test14238.d(26): `x` declared here --- */ // https://issues.dlang.org/show_bug.cgi?id=14238 @safe: -alias Fn = ref int delegate() return; +alias Fn = ref int delegate() return @nogc; -ref int foo(return scope Fn fn) +ref int call(return scope Fn fn) @nogc { return fn(); // Ok } @@ -20,10 +22,14 @@ ref int foo2(scope Fn fn) { return fn(); // Error } -ref int bar() { +ref int bar() @nogc { int x; ref int baz() { return x; } - return foo(&baz); + + if (x == 0) + return call(&baz); + else + return (&baz)(); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18644.d b/gcc/testsuite/gdc.test/fail_compilation/test18644.d index 430967ee4a490dee284191421d4a0dd56ead648e..201e44694edcace48796832c829fea5e8429b57c 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test18644.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test18644.d @@ -1,9 +1,9 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test18644.d(15): Error: storing reference to stack allocated value returned by `foo()` into allocated memory causes it to escape -fail_compilation/test18644.d(16): Error: escaping reference to stack allocated value returned by `foo()` -fail_compilation/test18644.d(22): Error: escaping reference to stack allocated value returned by `foo()` +fail_compilation/test18644.d(15): Error: nested function `foo` returns `scope` values and escapes them into allocated memory +fail_compilation/test18644.d(16): Error: escaping local variable through nested function `foo` +fail_compilation/test18644.d(22): Error: escaping local variable through nested function `foo` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22977.d b/gcc/testsuite/gdc.test/fail_compilation/test22977.d index 87bb19cc711c8e0b856e300079a443e56c390105..9478ec989d87ae543ad3d2f8b7455f117490175e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test22977.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test22977.d @@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- fail_compilation/test22977.d(16): Error: escaping local variable through nested function `scfunc` -fail_compilation/test22977.d(22): Error: escaping reference to stack allocated value returned by `scfunc2()` +fail_compilation/test22977.d(22): Error: escaping local variable through nested function `scfunc2` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23022.d b/gcc/testsuite/gdc.test/fail_compilation/test23022.d index 8c4eca9c5637cb5fadb9c277ec25260dd3439d95..db1f4a712c1d705077ba2307ee5055c38d52444f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test23022.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test23022.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test23022.d(14): Error: scope parameter `p` may not be returned +fail_compilation/test23022.d(14): Error: returning `p` escapes a reference to variadic parameter `p` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test24680.d b/gcc/testsuite/gdc.test/fail_compilation/test24680.d new file mode 100644 index 0000000000000000000000000000000000000000..50efba41344e4a55752bf3fefb2ea9b473e41038 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test24680.d @@ -0,0 +1,20 @@ +/** +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test24680.d(19): Error: returning `c.peek(buf[])` escapes a reference to local variable `buf` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24680 + +class C +{ + final auto peek(ubyte[] buf) { return buf; } +} + +@safe escape(C c) +{ + ubyte[5] buf; + return c.peek(buf[]); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d b/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d index 4c8576c88d9dcba9f8d77febf53b95035cd26f3d..ea929bcc45cd91cee59b6d67942678218265201a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d +++ b/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d @@ -17,14 +17,14 @@ fail_compilation/typeerrors.d(52): Error: cannot have associative array of `void fail_compilation/typeerrors.d(54): Error: cannot have parameter of type `void` fail_compilation/typeerrors.d(56): Error: slice `[1..5]` is out of range of [0..4] fail_compilation/typeerrors.d(57): Error: slice `[2..1]` is out of range of [0..4] +fail_compilation/typeerrors.d(59): Error: variable `typeerrors.foo.globalC` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope` +fail_compilation/typeerrors.d(59): Error: variable `typeerrors.foo.globalC` reference to `scope class` must be `scope` +fail_compilation/typeerrors.d(60): Error: variable `typeerrors.foo.manifestC` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope` +fail_compilation/typeerrors.d(60): Error: variable `typeerrors.foo.manifestC` reference to `scope class` must be `scope` --- */ - - - - template tuple(T...) { alias T tuple; } void bar(); @@ -55,4 +55,7 @@ void foo() alias T2 = T[1 .. 5]; alias T3 = T[2 .. 1]; + + static C globalC; + enum C manifestC = new C(); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/verrors5.d b/gcc/testsuite/gdc.test/fail_compilation/verrors5.d index 424d4f7622dbc8b8ad3d1a16b7c0ba250f91ccbb..74068610add8dda2292ae975cabc0c10c65dc7ed 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/verrors5.d +++ b/gcc/testsuite/gdc.test/fail_compilation/verrors5.d @@ -36,5 +36,6 @@ fail_compilation/verrors5.d(6): Error: undefined identifier `T` fail_compilation/verrors5.d(7): Error: undefined identifier `T` fail_compilation/verrors5.d(8): Error: undefined identifier `T` fail_compilation/verrors5.d(9): Error: undefined identifier `T` +error limit (5) reached, use `-verrors=0` to show all --- */ diff --git a/gcc/testsuite/gdc.test/runnable/declaration.d b/gcc/testsuite/gdc.test/runnable/declaration.d index 5be76fac6d7486bf2c7e67632bc94b6fc30ef5b1..a6642ea66777b95b65273d4ca4ae771518a0b0bf 100644 --- a/gcc/testsuite/gdc.test/runnable/declaration.d +++ b/gcc/testsuite/gdc.test/runnable/declaration.d @@ -406,6 +406,33 @@ void test13950() /***************************************************/ + +void testlocalref() +{ + int x = 4; + ref int rx = x; + rx = 5; + assert(x == 5); + ref int r2 = rx; + r2 = 6; + assert(x == 6); +} + +/***************************************************/ + +int global; + +ref int tgr() { return global; } + +void testglobalref() +{ + auto i = tgr(); + i = 1; + assert(global == 0); +} + +/***************************************************/ + int main() { test6475(); @@ -419,6 +446,8 @@ int main() test10142(); test11421(); test13950(); + testlocalref(); + testglobalref(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/link11069a.d b/gcc/testsuite/gdc.test/runnable/link11069a.d index e31cfa0bcf6bd0b6c897d520479e4be38775bb70..429ae8da7ec3ad0c779f2642380f4fb99c68f6b2 100644 --- a/gcc/testsuite/gdc.test/runnable/link11069a.d +++ b/gcc/testsuite/gdc.test/runnable/link11069a.d @@ -1,5 +1,5 @@ // EXTRA_FILES: imports/std11069array.d imports/std11069container.d imports/std11069range.d imports/std11069typecons.d -// REQUIRED_ARGS: -noboundscheck +// REQUIRED_ARGS: -boundscheck=off // <-- To remove necessity of _D7imports13std11069array7__arrayZ class Bar diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index d458bea5e1a593bfcd3455a9fe99cdc29f4c5b09..a6072c4570d5cb6a7e8b4ee2abd2940838dffc0f 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -66b93fc24a7ab5e2a8aa7f53c613df4abddc188b +34875cd6e1faa42e84ae953c0485ef524fe67e38 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/core/cpuid.d b/libphobos/libdruntime/core/cpuid.d index 62edbac34f33ffc88ca2a9ba9c628c9c6e8ded3b..4c2eb2adc00119a573232fe68680caba1ab2c869 100644 --- a/libphobos/libdruntime/core/cpuid.d +++ b/libphobos/libdruntime/core/cpuid.d @@ -1038,9 +1038,9 @@ bool hasCPUID() void cpuidX86() { - datacache[0].size = 8; - datacache[0].associativity = 2; - datacache[0].lineSize = 32; + datacache[0].size = 8; + datacache[0].associativity = 2; + datacache[0].lineSize = 32; } } diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d index 4405dec263987a56e387fe35d81a0bf9a991de97..c197262384f5ee684a92e10e19dc554975f14205 100644 --- a/libphobos/libdruntime/core/demangle.d +++ b/libphobos/libdruntime/core/demangle.d @@ -3187,9 +3187,7 @@ private struct BufSlice size_t from; size_t to; - @safe: - pure: - nothrow: + @safe pure nothrow: @disable this(); @@ -3209,7 +3207,7 @@ private struct BufSlice this.to = to; } - invariant() + invariant { if (buf is null) { diff --git a/libphobos/libdruntime/core/internal/array/concatenation.d b/libphobos/libdruntime/core/internal/array/concatenation.d index 4a05b50fcff436fe69392f3b9094e14c0ddaf3a0..3063c4c0ea063ac02c65e7e0b3a0e6aed230f1fa 100644 --- a/libphobos/libdruntime/core/internal/array/concatenation.d +++ b/libphobos/libdruntime/core/internal/array/concatenation.d @@ -62,7 +62,7 @@ Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { size_t i = 0; foreach (ref from; froms) - static if (is (typeof(from) : T)) + static if (is(typeof(from) : T)) copyEmplace(cast(T) from, res[i++]); else { diff --git a/libphobos/libdruntime/core/internal/array/construction.d b/libphobos/libdruntime/core/internal/array/construction.d index 8f0323a054b4d8dc8feabfc7a2a6e6ba14064669..8098597a72a95a2563224556cf3944dcb255ccfe 100644 --- a/libphobos/libdruntime/core/internal/array/construction.d +++ b/libphobos/libdruntime/core/internal/array/construction.d @@ -530,7 +530,7 @@ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared=false) @trust if (dims.length == 1) { auto r = _d_newarrayT!UnqT(dim, isShared); - return *cast(void[]*)(&r); + return *cast(void[]*)&r; } auto allocSize = (void[]).sizeof * dim; diff --git a/libphobos/libdruntime/core/internal/atomic.d b/libphobos/libdruntime/core/internal/atomic.d index 6242d76ba30a70f07b1c7fa5ebfb0b9415023dfe..36cf6891feb4e2393cbd9cb29d18e536f3c990ed 100644 --- a/libphobos/libdruntime/core/internal/atomic.d +++ b/libphobos/libdruntime/core/internal/atomic.d @@ -983,7 +983,7 @@ else version (GNU) // Internal static mutex reference. private AtomicMutex* _getAtomicMutex() @trusted @nogc nothrow { - __gshared static AtomicMutex mutex; + __gshared AtomicMutex mutex; return &mutex; } diff --git a/libphobos/libdruntime/core/internal/convert.d b/libphobos/libdruntime/core/internal/convert.d index 92eb243ec1a9a74145944df72b22d51dd091b51d..7bac8359a77a6b7c73c629bff97a741926452a0e 100644 --- a/libphobos/libdruntime/core/internal/convert.d +++ b/libphobos/libdruntime/core/internal/convert.d @@ -695,7 +695,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && } else { - return (cast(const(ubyte)*)(&val))[0 .. T.sizeof]; + return (cast(const(ubyte)*)&val)[0 .. T.sizeof]; } } else if (__ctfe) @@ -715,7 +715,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && } else { - return (cast(const(ubyte)*)(&val))[0 .. T.sizeof]; + return (cast(const(ubyte)*)&val)[0 .. T.sizeof]; } } diff --git a/libphobos/libdruntime/core/internal/gc/bits.d b/libphobos/libdruntime/core/internal/gc/bits.d index aa94e4076428a2698d76e50fa7c5b5c844133868..a060e65c8e14ba203362aa30255c4c1e909fe3eb 100644 --- a/libphobos/libdruntime/core/internal/gc/bits.d +++ b/libphobos/libdruntime/core/internal/gc/bits.d @@ -134,7 +134,7 @@ struct GCBits } else { - auto pos = i >> BITS_SHIFT; + const pos = i >> BITS_SHIFT; auto pdata = cast(shared)(data + pos); auto mask = BITS_1 << (i & BITS_MASK); auto state = *pdata; diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index b1b270799aa7fa2e7ab8091c4bbbc59cb95464e8..dd6f92a8a7ab5fc41d59dab8cc507dae28adbdd7 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -404,7 +404,7 @@ class ConservativeGC : GC p = sentinel_sub(p); if (p != pool.findBase(p)) return 0; - auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; + const biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; oldb = pool.getBits(biti); pool.setBits(biti, mask); @@ -447,7 +447,7 @@ class ConservativeGC : GC p = sentinel_sub(p); if (p != pool.findBase(p)) return 0; - auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; + const biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; oldb = pool.getBits(biti); pool.clrBits(biti, mask); @@ -784,25 +784,25 @@ class ConservativeGC : GC return 0; auto lpool = cast(LargeObjectPool*) pool; - size_t pagenum = lpool.pagenumOf(p); + const pagenum = lpool.pagenumOf(p); if (lpool.pagetable[pagenum] != Bins.B_PAGE) return 0; - size_t psz = lpool.bPageOffsets[pagenum]; + uint psz = lpool.bPageOffsets[pagenum]; assert(psz > 0); - auto minsz = lpool.numPages(minsize); - auto maxsz = lpool.numPages(maxsize); + const minsz = lpool.numPages(minsize); + const maxsz = lpool.numPages(maxsize); if (pagenum + psz >= lpool.npages) return 0; if (lpool.pagetable[pagenum + psz] != Bins.B_FREE) return 0; - size_t freesz = lpool.bPageOffsets[pagenum + psz]; + const freesz = lpool.bPageOffsets[pagenum + psz]; if (freesz < minsz) return 0; - size_t sz = freesz > maxsz ? maxsz : freesz; + const sz = freesz > maxsz ? maxsz : freesz; invalidate((pool.baseAddr + (pagenum + psz) * PAGESIZE)[0 .. sz * PAGESIZE], 0xF0, true); memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz); lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz); @@ -1609,7 +1609,7 @@ struct Gcx void Invariant() const { } debug(INVARIANT) - invariant() + invariant { if (initialized) { @@ -3171,8 +3171,7 @@ Lmark: { // first, we find the Pool this block is in, then check to see if the // mark bit is clear. - auto pool = findPool(addr); - if (pool) + if (auto pool = findPool(addr)) { auto offset = cast(size_t)(addr - pool.baseAddr); auto pn = offset / PAGESIZE; @@ -3924,7 +3923,7 @@ struct Pool void Invariant() const {} debug(INVARIANT) - invariant() + invariant { if (baseAddr) { diff --git a/libphobos/libdruntime/core/stdc/fenv.d b/libphobos/libdruntime/core/stdc/fenv.d index 6cd75f3a791e335c5e1f62d06c04187b82b32836..b9b10aae3d93e777cec69960d80acc590a7f3533 100644 --- a/libphobos/libdruntime/core/stdc/fenv.d +++ b/libphobos/libdruntime/core/stdc/fenv.d @@ -959,7 +959,7 @@ version (CRuntime_Microsoft) // supported since MSVCRT 12 (VS 2013) only double num; double denom; } - static __gshared immutable(Entry[5]) table = + static immutable Entry[5] table = [ // Raise exception by evaluating num / denom: { FE_INVALID, 0.0, 0.0 }, { FE_DIVBYZERO, 1.0, 0.0 }, diff --git a/libphobos/libdruntime/core/stdc/stdatomic.d b/libphobos/libdruntime/core/stdc/stdatomic.d index 72e037f7e92968e4a3d68bd044097972219877f5..51968af8c5fbc3dc2b41e10e6a755860214c519f 100644 --- a/libphobos/libdruntime/core/stdc/stdatomic.d +++ b/libphobos/libdruntime/core/stdc/stdatomic.d @@ -18,6 +18,7 @@ module core.stdc.stdatomic; import core.atomic : MemoryOrder; import core.internal.atomic; import core.stdc.config; +import core.stdc.stddef; import core.stdc.stdint; @safe nothrow @nogc: @@ -54,7 +55,7 @@ enum /// ATOMIC_CHAR32_T_LOCK_FREE = IsAtomicLockFree!dchar ? 2 : 0, /// - ATOMIC_WCHAR_T_LOCK_FREE = ATOMIC_CHAR16_T_LOCK_FREE, + ATOMIC_WCHAR_T_LOCK_FREE = IsAtomicLockFree!wchar_t ? 2 : 0, /// ATOMIC_SHORT_LOCK_FREE = IsAtomicLockFree!short ? 2 : 0, /// @@ -365,7 +366,7 @@ alias atomic_char16_t = shared(wchar); /// alias atomic_char32_t = shared(dchar); /// -alias atomic_wchar_t = shared(wchar); +alias atomic_wchar_t = shared(wchar_t); /// alias atomic_int_least8_t = shared(int_least8_t); diff --git a/libphobos/libdruntime/core/stdc/stdint.d b/libphobos/libdruntime/core/stdc/stdint.d index 1776269378f809344ce7ac215d87962c028ca20a..4f9b98cd8ce079311224c1b4e707006887c5ddbb 100644 --- a/libphobos/libdruntime/core/stdc/stdint.d +++ b/libphobos/libdruntime/core/stdc/stdint.d @@ -480,13 +480,21 @@ enum int_fast64_t INT_FAST64_MIN = int_fast64_t.min; /// enum int_fast64_t INT_FAST64_MAX = int_fast64_t.max; +/// +enum uint_fast8_t UINT_FAST8_MIN = uint_fast8_t.min; /// enum uint_fast8_t UINT_FAST8_MAX = uint_fast8_t.max; /// +enum uint_fast16_t UINT_FAST16_MIN = uint_fast16_t.min; +/// enum uint_fast16_t UINT_FAST16_MAX = uint_fast16_t.max; /// +enum uint_fast32_t UINT_FAST32_MIN = uint_fast32_t.min; +/// enum uint_fast32_t UINT_FAST32_MAX = uint_fast32_t.max; /// +enum uint_fast64_t UINT_FAST64_MIN = uint_fast64_t.min; +/// enum uint_fast64_t UINT_FAST64_MAX = uint_fast64_t.max; /// diff --git a/libphobos/libdruntime/core/stdc/wctype.d b/libphobos/libdruntime/core/stdc/wctype.d index b37e8322dc34f40d98878dbc00b7a0e2729895ce..88b6a01245fe81159a0413aaf80453d45f08d9ab 100644 --- a/libphobos/libdruntime/core/stdc/wctype.d +++ b/libphobos/libdruntime/core/stdc/wctype.d @@ -14,6 +14,7 @@ module core.stdc.wctype; +import core.stdc.config; public import core.stdc.wchar_; // for wint_t, WEOF extern (C): @@ -21,10 +22,41 @@ extern (C): nothrow: @nogc: -/// -alias wchar_t wctrans_t; -/// -alias wchar_t wctype_t; +version (CRuntime_Glibc) +{ + /// + alias wctype_t = c_ulong; + /// + alias wctrans_t = const(int)*; +} +else version (CRuntime_Musl) +{ + /// + alias wctype_t = c_ulong; + /// + alias wctrans_t = const(int)*; +} +else version (FreeBSD) +{ + /// + alias wctype_t = c_ulong; + /// + alias wctrans_t = int; +} +else version (CRuntime_Bionic) +{ + /// + alias wctype_t = c_long; + /// + alias wctrans_t = const(void)*; +} +else +{ + /// + alias wchar_t wctrans_t; + /// + alias wchar_t wctype_t; +} /// pure int iswalnum(wint_t wc); @@ -63,3 +95,17 @@ pure wint_t towupper(wint_t wc); wint_t towctrans(wint_t wc, wctrans_t desc); /// @system wctrans_t wctrans(const scope char* property); + +unittest +{ + assert(iswalpha('A')); + assert(!iswalpha('0')); + wctype_t alpha = wctype("alpha"); + assert(alpha); + wctrans_t tolower = wctrans("tolower"); + assert(tolower); + assert(iswctype('A', alpha)); + assert(!iswctype('0', alpha)); + assert(towctrans('A', tolower) == 'a'); + assert(towctrans('0', tolower) == '0'); +} diff --git a/libphobos/libdruntime/core/stdcpp/string_view.d b/libphobos/libdruntime/core/stdcpp/string_view.d index fd79a121be51f2ca43c8fffee4674c020d38a1c4..986fa7ee273a3ff801e4de18bb067671fdbeb6d6 100644 --- a/libphobos/libdruntime/core/stdcpp/string_view.d +++ b/libphobos/libdruntime/core/stdcpp/string_view.d @@ -37,7 +37,7 @@ extern(C++, struct) struct char_traits(CharT) {} /** * D language counterpart to C++ std::basic_string_view. * -* C++ reference: $(LINK2 hhttps://en.cppreference.com/w/cpp/string/basic_string_view) +* C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string_view, std::basic_string_view) */ extern(C++, class) struct basic_string_view(T, Traits = char_traits!T) { diff --git a/libphobos/libdruntime/core/sys/posix/signal.d b/libphobos/libdruntime/core/sys/posix/signal.d index a8b7f7511a92c897ebbc9f0997bd41cd11d9ca1a..ed4ec1270d9003709874da5da5bd225b8b6bf8c7 100644 --- a/libphobos/libdruntime/core/sys/posix/signal.d +++ b/libphobos/libdruntime/core/sys/posix/signal.d @@ -136,7 +136,7 @@ version (Solaris) import core.sys.posix.unistd; @property int SIGRTMIN() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = cast(int)sysconf(_SC_SIGRT_MIN); } @@ -144,7 +144,7 @@ version (Solaris) } @property int SIGRTMAX() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = cast(int)sysconf(_SC_SIGRT_MAX); } @@ -180,7 +180,7 @@ else version (linux) } @property int SIGRTMIN() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = __libc_current_sigrtmin(); } @@ -188,7 +188,7 @@ else version (linux) } @property int SIGRTMAX() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = __libc_current_sigrtmax(); } diff --git a/libphobos/libdruntime/core/sys/posix/sys/resource.d b/libphobos/libdruntime/core/sys/posix/sys/resource.d index 3a9a1876393d5cb10e50c6a5ad85b5b4a549189c..b75c794e926a8084a37bc62aa03fa117f5bb2a9e 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/resource.d +++ b/libphobos/libdruntime/core/sys/posix/sys/resource.d @@ -82,14 +82,14 @@ version (linux) } static if (__USE_FILE_OFFSET64) - alias ulong rlim_t; + alias ulong rlim_t; else - alias c_ulong rlim_t; + alias c_ulong rlim_t; static if (__USE_FILE_OFFSET64) enum RLIM_INFINITY = 0xffffffffffffffffUL; else - enum RLIM_INFINITY = cast(c_ulong)(~0UL); + enum RLIM_INFINITY = cast(c_ulong)~0UL; enum RLIM_SAVED_MAX = RLIM_INFINITY; enum RLIM_SAVED_CUR = RLIM_INFINITY; @@ -163,7 +163,7 @@ else version (Darwin) enum { - RLIM_INFINITY = ((cast(ulong) 1 << 63) - 1), + RLIM_INFINITY = ((1UL << 63) - 1), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -205,7 +205,7 @@ else version (FreeBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -262,7 +262,7 @@ else version (NetBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -319,7 +319,7 @@ else version (OpenBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -378,7 +378,7 @@ else version (DragonFlyBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } diff --git a/libphobos/libdruntime/core/sys/windows/dll.d b/libphobos/libdruntime/core/sys/windows/dll.d index 6a003b5c633ac70622013cad4df474e9a304d4c6..7c601bb2e3fadd4564d83083390d1472dc45dbd1 100644 --- a/libphobos/libdruntime/core/sys/windows/dll.d +++ b/libphobos/libdruntime/core/sys/windows/dll.d @@ -117,19 +117,19 @@ version (Win32) // _NtdllBaseTag - tag used for RtlAllocateHeap // _LdrpTlsList - root of the double linked list with TlsList entries - static __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData - - static __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize - static __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize - static __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop; - static __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread - static __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls - static __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls - static __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls - static __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries - static __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag - static __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag - static __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList + __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData + + __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize + __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize + __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop; + __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread + __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls + __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls + __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls + __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries + __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag + __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag + __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList static LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) nothrow { diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d index c3192fc39f9cd53b317e5086a5f6090d31b7c836..dc79141efa7b74d8d52c6b18155db01797b05148 100644 --- a/libphobos/libdruntime/core/time.d +++ b/libphobos/libdruntime/core/time.d @@ -701,9 +701,10 @@ public: version (CoreUnittest) unittest { - foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) + alias Types = AliasSeq!(Duration, const Duration, immutable Duration); + foreach (D; Types) { - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) + foreach (E; Types) { assert((cast(D)Duration(5)) + (cast(E)Duration(7)) == Duration(12)); assert((cast(D)Duration(5)) - (cast(E)Duration(7)) == Duration(-2)); diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index 9bc929ce8efa889b4a8378a3a71993a599118ac0..565f6a1b15c6b8e8bd124f7858e4a0a4a1c1872c 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -3191,6 +3191,10 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc sum += v; assert(sum == 3); + + foreach (ref v; dict.byValue) + v++; + assert(dict == ["k1": 2, "k2": 3]); } /*********************************** @@ -3214,7 +3218,7 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc *--- * * Note that this is a low-level interface to iterating over the associative - * array and is not compatible withth the + * array and is not compatible with the * $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) type in Phobos. * For compatibility with `Tuple`, use * $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead. @@ -3278,8 +3282,11 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc assert(e.key[1] == e.value + '0'); sum += e.value; } - assert(sum == 3); + + foreach (e; dict.byKeyValue) + e.value++; + assert(dict == ["k1": 2, "k2": 3]); } /*********************************** @@ -3451,8 +3458,8 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property } /*********************************** - * Looks up key; if it exists returns corresponding value else evaluates and - * returns defaultValue. + * If `key` is in `aa`, returns corresponding value; otherwise it evaluates and + * returns `defaultValue`. * Params: * aa = The associative array. * key = The key. @@ -3481,8 +3488,8 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) } /*********************************** - * Looks up key; if it exists returns corresponding value else evaluates - * value, adds it to the associative array and returns it. + * If `key` is in `aa`, returns corresponding value; otherwise it evaluates + * `value`, adds it to the associative array and returns it. * Params: * aa = The associative array. * key = The key. diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d index 26c16d3af0a400aa1d13f24b1c208b0ba30df1cc..2bddeaf41d7c094a006baa3e5b4a545fab76564e 100644 --- a/libphobos/libdruntime/rt/aaA.d +++ b/libphobos/libdruntime/rt/aaA.d @@ -343,11 +343,11 @@ immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, pos++; } if (keybits > 0) - rtinfoData[pos] = mixin(src) & ((cast(size_t) 1 << keybits) - 1); + rtinfoData[pos] = mixin(src) & ((size_t(1) << keybits) - 1); } if (keyinfo is rtinfoHasPointers) - copyKeyInfo!"~cast(size_t) 0"(); + copyKeyInfo!"~size_t(0)"(); else if (keyinfo !is rtinfoNoPointers) copyKeyInfo!"keyinfo[pos]"(); @@ -373,11 +373,11 @@ immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, pos++; } if (endoff > 0) - rtinfoData[dstpos] &= ((cast(size_t) 1 << endoff) - 1); + rtinfoData[dstpos] &= (size_t(1) << endoff) - 1; } if (valinfo is rtinfoHasPointers) - copyValInfo!"~cast(size_t) 0"(); + copyValInfo!"~size_t(0)"(); else if (valinfo !is rtinfoNoPointers) copyValInfo!"valinfo[pos]"(); @@ -977,7 +977,7 @@ unittest aa1 = null; aa2 = null; aa3 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]); + GC.runFinalizers((cast(char*)&entryDtor)[0 .. 1]); assert(T.dtor == 6 && T.postblit == 2); } diff --git a/libphobos/libdruntime/rt/cast_.d b/libphobos/libdruntime/rt/cast_.d index 43ddd5a9316490cc186a8216739b2eea1ac14559..c7d8bbaab353bb6ab2334df92595031f537e0344 100644 --- a/libphobos/libdruntime/rt/cast_.d +++ b/libphobos/libdruntime/rt/cast_.d @@ -167,7 +167,7 @@ void* _d_paint_cast(Object o, ClassInfo c) { /* If o is really an instance of c, just do a paint */ - auto p = (o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null); + auto p = o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null; debug assert(cast(void*)p is cast(void*)_d_dynamic_cast(o, c)); return cast(void*)p; } diff --git a/libphobos/libdruntime/rt/config.d b/libphobos/libdruntime/rt/config.d index a6605f4d60374e411e0bf5e8092069f9a287b66a..912b8fe36ad1480fc111e112ebd6dde2478b4824 100644 --- a/libphobos/libdruntime/rt/config.d +++ b/libphobos/libdruntime/rt/config.d @@ -129,8 +129,7 @@ string rt_envvarsOption(string opt, scope rt_configCallBack dg) @nogc nothrow var[4 + i] = cast(char) toupper(c); var[4 + opt.length] = 0; - auto p = getenv(var.ptr); - if (p) + if (auto p = getenv(var.ptr)) { string s = dg(cast(string) p[0 .. strlen(p)]); if (s != null) diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index 052b859fd4905a746a603c74d04966af910fe1ff..be416c47906a7103da54c377ba18a5b63f2915cb 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -607,7 +607,7 @@ extern (C) void _d_print_throwable(Throwable t) void sink(in char[] s) scope nothrow { if (!s.length) return; - int swlen = MultiByteToWideChar( + const swlen = MultiByteToWideChar( CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0); if (!swlen) return; @@ -615,7 +615,7 @@ extern (C) void _d_print_throwable(Throwable t) (this.len + swlen + 1) * WCHAR.sizeof); if (!newPtr) return; ptr = newPtr; - auto written = MultiByteToWideChar( + const written = MultiByteToWideChar( CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen); len += written; } @@ -633,10 +633,6 @@ extern (C) void _d_print_throwable(Throwable t) return _fdToHandle(fd); } - auto hStdErr = windowsHandle(fileno(stderr)); - CONSOLE_SCREEN_BUFFER_INFO sbi; - bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0; - // ensure the exception is shown at the beginning of the line, while also // checking whether stderr is a valid file int written = fprintf(stderr, "\n"); @@ -653,22 +649,22 @@ extern (C) void _d_print_throwable(Throwable t) // Avoid static user32.dll dependency for console applications // by loading it dynamically as needed - auto user32 = LoadLibraryW("user32.dll"); - if (user32) + if (auto user32 = LoadLibraryW("user32.dll")) { alias typeof(&MessageBoxW) PMessageBoxW; - auto pMessageBoxW = cast(PMessageBoxW) - GetProcAddress(user32, "MessageBoxW"); - if (pMessageBoxW) + if (auto pMessageBoxW = cast(PMessageBoxW) GetProcAddress(user32, "MessageBoxW")) pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR); + FreeLibrary(user32); } - FreeLibrary(user32); caption.free(); buf.free(); } return; } - else if (isConsole) + auto hStdErr = windowsHandle(fileno(stderr)); + CONSOLE_SCREEN_BUFFER_INFO sbi = void; + const isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0; + if (isConsole) { WSink buf; formatThrowable(t, &buf.sink); @@ -676,10 +672,9 @@ extern (C) void _d_print_throwable(Throwable t) if (buf.ptr) { uint codepage = GetConsoleOutputCP(); - int slen = WideCharToMultiByte(codepage, 0, + const slen = WideCharToMultiByte(codepage, 0, buf.ptr, cast(int)buf.len, null, 0, null, null); - auto sptr = cast(char*)malloc(slen * char.sizeof); - if (sptr) + if (auto sptr = cast(char*)malloc(slen * char.sizeof)) { WideCharToMultiByte(codepage, 0, buf.ptr, cast(int)buf.len, sptr, slen, null, null); diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 676f88d5ae403e10c6ba715541daba6e6c8cb186..a8d084060cf0ade6b1cb44ee51a44056b5dfcc02 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -1941,15 +1941,15 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n __setArrayAllocLength(info, newsize, isshared, tinext); if (!isshared) __insertBlkInfoCache(info, bic); - auto newdata = cast(byte *)__arrayStart(info); + auto newdata = cast(byte*)__arrayStart(info); memcpy(newdata, px.ptr, length * sizeelem); // do postblit processing __doPostblit(newdata, length * sizeelem, tinext); - (cast(void **)(&px))[1] = newdata; + (cast(void**)&px)[1] = newdata; } L1: - *cast(size_t *)&px = newlength; + *cast(size_t*)&px = newlength; return px; } @@ -2417,14 +2417,14 @@ deprecated unittest // associative arrays import rt.aaA : entryDtor; // throw away all existing AA entries with dtor - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + GC.runFinalizers((cast(char*)&entryDtor)[0..1]); S1[int] aa1; aa1[0] = S1(0); aa1[1] = S1(1); dtorCount = 0; aa1 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + GC.runFinalizers((cast(char*)&entryDtor)[0..1]); assert(dtorCount == 2); int[S1] aa2; @@ -2433,7 +2433,7 @@ deprecated unittest aa2[S1(2)] = 2; dtorCount = 0; aa2 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + GC.runFinalizers((cast(char*)&entryDtor)[0..1]); assert(dtorCount == 3); S1[2][int] aa3; @@ -2441,7 +2441,7 @@ deprecated unittest aa3[1] = [S1(1),S1(3)]; dtorCount = 0; aa3 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + GC.runFinalizers((cast(char*)&entryDtor)[0..1]); assert(dtorCount == 4); } diff --git a/libphobos/libdruntime/rt/monitor_.d b/libphobos/libdruntime/rt/monitor_.d index cbe2a4844023c59097ad69a1d27313ead7c637ce..3a9c1f7cc2ecbecb8d9961dc6c9031739d8dc6cd 100644 --- a/libphobos/libdruntime/rt/monitor_.d +++ b/libphobos/libdruntime/rt/monitor_.d @@ -27,7 +27,7 @@ do auto m = ensureMonitor(cast(Object) owner); if (m.impl is null) { - atomicOp!("+=")(m.refs, cast(size_t) 1); + atomicOp!"+="(m.refs, size_t(1)); } // Assume the monitor is garbage collected and simply copy the reference. ownee.__monitor = owner.__monitor; @@ -44,7 +44,7 @@ extern (C) void _d_monitordelete(Object h, bool det) // let the GC collect the monitor setMonitor(h, null); } - else if (!atomicOp!("-=")(m.refs, cast(size_t) 1)) + else if (!atomicOp!"-="(m.refs, size_t(1))) { // refcount == 0 means unshared => no synchronization required disposeEvent(cast(Monitor*) m, h); @@ -65,7 +65,7 @@ extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow // let the GC collect the monitor setMonitor(h, null); } - else if (!atomicOp!("-=")(m.refs, cast(size_t) 1)) + else if (!atomicOp!"-="(m.refs, size_t(1))) { // refcount == 0 means unshared => no synchronization required deleteMonitor(cast(Monitor*) m); @@ -231,7 +231,7 @@ private: return *cast(shared Monitor**)&h.__monitor; } -private shared(Monitor)* getMonitor(Object h) pure @nogc +shared(Monitor)* getMonitor(Object h) pure @nogc { return atomicLoad!(MemoryOrder.acq)(h.monitor); } diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d index 730649ef4817c55cd11508b5971ef082c7216ad2..9c5af477cbb885403b693e4c66bceead8062ad94 100644 --- a/libphobos/libdruntime/rt/util/typeinfo.d +++ b/libphobos/libdruntime/rt/util/typeinfo.d @@ -203,10 +203,10 @@ detect if we need to override. The overriding initializer should be nonzero. private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base) if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) { - const: nothrow: pure: @trusted: + const: nothrow pure @trusted: // Returns the type name. - override string toString() const pure nothrow @safe { return T.stringof; } + override string toString() const @safe { return T.stringof; } // `getHash` is the same for `Base` and `T`, introduce it just once. static if (is(T == Base)) @@ -390,16 +390,16 @@ unittest // void class TypeInfo_v : TypeInfoGeneric!ubyte { - const: nothrow: pure: @trusted: + const nothrow pure @trusted: - override string toString() const pure nothrow @safe { return "void"; } + override string toString() const @safe { return "void"; } override size_t getHash(scope const void* p) { assert(0); } - override @property uint flags() nothrow pure + override @property uint flags() { return 1; } @@ -640,7 +640,7 @@ class TypeInfo_n : TypeInfo override @property size_t tsize() { return typeof(null).sizeof; } - override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; } + override const(void)[] initializer() @trusted { return (cast(void*)null)[0 .. size_t.sizeof]; } override void swap(void*, void*) {} diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 46e244379e16ce05a04989b3db105f90a7fee868..76975e01c29a18edf8e0230f6bd188ead3a51789 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -0c28620c301c9ae3136b1e1e5af55c290dbc7aae +ebd24da8add9243c52e5cb346dcdf7acf3e6cbac 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/mutation.d b/libphobos/src/std/algorithm/mutation.d index fbef28e5d563afe6535461d1e505023b4c4abc98..e434d248edbfb1ec59790445fe866881470876a8 100644 --- a/libphobos/src/std/algorithm/mutation.d +++ b/libphobos/src/std/algorithm/mutation.d @@ -1071,10 +1071,20 @@ Params: copy is performed. */ void move(T)(ref T source, ref T target) +if (__traits(compiles, target = T.init)) { moveImpl(target, source); } +/// ditto +template move(T) +if (!__traits(compiles, imported!"std.traits".lvalueOf!T = T.init)) +{ + /// + deprecated("Can't move into `target` as `" ~ T.stringof ~ "` can't be assigned") + void move(ref T source, ref T target) => moveImpl(target, source); +} + /// For non-struct types, `move` just performs `target = source`: @safe unittest { @@ -1184,6 +1194,19 @@ pure nothrow @safe @nogc unittest assert(s53 is s51); } +@system unittest +{ + static struct S + { + immutable int i; + ~this() @safe {} + } + alias ol = __traits(getOverloads, std.algorithm.mutation, "move", true)[1]; + static assert(__traits(isDeprecated, ol!S)); + // uncomment after deprecation + //static assert(!__traits(compiles, { S a, b; move(a, b); })); +} + /// Ditto T move(T)(return scope ref T source) { diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d index c5b085d1037f232375dcc5abaf7060dbd0a5f26b..2d16c65111fc0aef135e8c7c21a52e790b283308 100644 --- a/libphobos/src/std/algorithm/sorting.d +++ b/libphobos/src/std/algorithm/sorting.d @@ -2164,12 +2164,12 @@ private void quickSortImpl(alias less, Range)(Range r, size_t depth) { import std.algorithm.comparison : min, max; import std.algorithm.mutation : swap, swapAt; - import std.conv : to; alias Elem = ElementType!(Range); - enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof); + enum int size = Elem.sizeof; + enum size_t shortSortGetsBetter = max(32, 1024 / size); static assert(shortSortGetsBetter >= 1, Elem.stringof ~ " " - ~ to!string(Elem.sizeof)); + ~ size.stringof); // partition while (r.length > shortSortGetsBetter) diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index 494fa297b4b8eb4f8ed8541697893da511230139..1ce4a64877748800fbdf3b3b3b6e10e0c11cc618 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -295,6 +295,19 @@ if (is(Range == U*, U) && isIterable!U && !isAutodecodableString!Range && !isInf R().array; } +// Test that `array(scope InputRange r)` returns a non-scope array +// https://issues.dlang.org/show_bug.cgi?id=23300 +@safe pure nothrow unittest +{ + @safe int[] fun() + { + import std.algorithm.iteration : map; + int[3] arr = [1, 2, 3]; + scope r = arr[].map!(x => x + 3); + return r.array; + } +} + /** Convert a narrow autodecoding string to an array type that fully supports random access. This is handled as a special case and always returns an array @@ -650,6 +663,8 @@ if (isInputRange!Values && isInputRange!Keys) alias ValueElement = ElementType!Values; static if (hasElaborateDestructor!ValueElement) ValueElement.init.__xdtor(); + + aa[key] = values.front; }))) { () @trusted { @@ -790,6 +805,20 @@ if (isInputRange!Values && isInputRange!Keys) assert(assocArray(1.iota, [UnsafeElement()]) == [0: UnsafeElement()]); } +@safe unittest +{ + struct ValueRange + { + string front() const @system; + @safe: + void popFront() {} + bool empty() const { return false; } + } + int[] keys; + ValueRange values; + static assert(!__traits(compiles, assocArray(keys, values))); +} + /** Construct a range iterating over an associative array by key/value tuples. diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d index 639b8214c8c977f58f059b96d8fe684c108f4db1..0993d34843fcf58b739372b86381cd96b1eff68f 100644 --- a/libphobos/src/std/bitmanip.d +++ b/libphobos/src/std/bitmanip.d @@ -1306,7 +1306,7 @@ public: /** Sets the bits of a slice of `BitArray` starting - at index `start` and ends at index ($D end - 1) + at index `start` and ends at index $(D end - 1) with the values specified by `val`. */ void opSliceAssign(bool val, size_t start, size_t end) @nogc pure nothrow diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d index ebdaba42a9d1290f298a548ed54190be977ef4a6..7526f2d7bb470414b7766dff4671a912344393d6 100644 --- a/libphobos/src/std/datetime/date.d +++ b/libphobos/src/std/datetime/date.d @@ -3776,7 +3776,7 @@ public: enforceValid!"months"(cast(Month) month); enforceValid!"days"(year, cast(Month) month, day); - _year = cast(short) year; + _year = year.castToYear; _month = cast(Month) month; _day = cast(ubyte) day; } @@ -3814,6 +3814,7 @@ public: assertThrown!DateTimeException(Date(1999, 10, 32)); assertThrown!DateTimeException(Date(1999, 11, 31)); assertThrown!DateTimeException(Date(1999, 12, 32)); + assertThrown!DateTimeException(Date(short.max+1, 1, 1)); assertNotThrown!DateTimeException(Date(1999, 1, 31)); assertNotThrown!DateTimeException(Date(1999, 2, 28)); @@ -3839,6 +3840,7 @@ public: assertThrown!DateTimeException(Date(-1, 2, 29)); assertThrown!DateTimeException(Date(-2, 2, 29)); assertThrown!DateTimeException(Date(-3, 2, 29)); + assertThrown!DateTimeException(Date(short.min-1, 1, 1)); } @@ -4128,7 +4130,7 @@ public: @property void year(int year) @safe pure { enforceValid!"days"(year, _month, _day); - _year = cast(short) year; + _year = year.castToYear; } /// @@ -4215,7 +4217,7 @@ public: { if (year <= 0) throw new DateTimeException("The given year is not a year B.C."); - _year = cast(short)((year - 1) * -1); + _year = castToYear((year - 1) * -1); } /// @@ -9689,6 +9691,16 @@ if (units == "days") assert(!valid!"days"(2017, 2, 29)); } +private short castToYear(int year, string file = __FILE__, size_t line = __LINE__) @safe pure +{ + import std.conv : to, ConvOverflowException; + import std.format : format; + + try + return year.to!short; + catch (ConvOverflowException) + throw new DateTimeException(format("year %s doesn't fit to Date.", year), file, line); +} /++ Params: diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d index b23891808f5d70b319404c389c2060c815096a0a..4b6f27d066d5517bead165f353bd7f5cd91a63b6 100644 --- a/libphobos/src/std/datetime/timezone.d +++ b/libphobos/src/std/datetime/timezone.d @@ -3339,7 +3339,7 @@ else version (Posix) Windows uses a different set of time zone names than the IANA time zone database does, and how they correspond to one another changes over time (particularly when Microsoft updates Windows). - $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) + $(HTTP github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml, windowsZones.xml) provides the current conversions (which may or may not match up with what's on a particular Windows box depending on how up-to-date it is), and parseTZConversions reads in those conversions from windowsZones.xml so that @@ -3358,7 +3358,7 @@ else version (Posix) Params: windowsZonesXMLText = The text from - $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) + $(HTTP github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml, windowsZones.xml) Throws: Exception if there is an error while parsing the given XML. @@ -3372,7 +3372,7 @@ else version (Posix) // and parse it so that it's guaranteed to be up-to-date, though // that has the downside that the code needs to worry about the // site being down or unicode.org changing the URL. - auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"; + auto url = "https://raw.githubusercontent.com/unicode-org/cldr/main/common/supplemental/windowsZones.xml"; auto conversions2 = parseTZConversions(std.net.curl.get(url)); -------------------- +/ @@ -3458,7 +3458,7 @@ TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure import std.algorithm.iteration : uniq; import std.algorithm.sorting : isSorted; - // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml + // Reduced text from https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml auto sampleFileText = `<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd"> diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index 58a667c1d853364a6d39e8ded0e516e4c460f728..c3024d799686d35c5edb54cc83f3270493ec6d30 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -1540,9 +1540,9 @@ version (StdUnittest) } /+ -Returns true if the field at index `i` in ($D T) shares its address with another field. +Returns true if the field at index `i` in $(D T) shares its address with another field. -Note: This does not merelly check if the field is a member of an union, but also that +Note: This does not merely check if the field is a member of an union, but also that it is not a single child. +/ package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof); diff --git a/libphobos/src/std/experimental/allocator/building_blocks/package.d b/libphobos/src/std/experimental/allocator/building_blocks/package.d index 6bc527d23748ba418a40803707801da16522f377..521d7ed0a47cabc159dd9b446c733947a7dc50a0 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/package.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/package.d @@ -43,7 +43,7 @@ alignedReallocate) APIs.)) $(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators customarily allocate memory in discretely-sized chunks. Therefore, a request for `n` bytes may result in a larger allocation. The extra memory allocated goes -unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation). +unused and adds to the so-called $(HTTPS en.wikipedia.org/wiki/Fragmentation_(computing)#Internal_fragmentation,internal fragmentation). The function `goodAllocSize(n)` returns the actual number of bytes that would be allocated upon a request for `n` bytes. This module defines a default implementation that returns `n` rounded up to a multiple of the allocator's @@ -137,7 +137,7 @@ thread-safe or not, this instance may be `shared`.)) $(H2 Sample Assembly) -The example below features an _allocator modeled after $(HTTP goo.gl/m7329l, +The example below features an _allocator modeled after $(HTTP jemalloc.net/, jemalloc), which uses a battery of free-list allocators spaced so as to keep internal fragmentation to a minimum. The `FList` definitions specify no bounds for the freelist because the `Segregator` does all size selection in diff --git a/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d index a23746a236b6187afac51c181721094a75c97d8d..736b1858e431392af2bd3e28e9af77bd7f350946 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/region.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d @@ -904,7 +904,7 @@ version (DragonFlyBSD) { // sbrk is deprecated in favor of mmap (we could implement a mmap + MAP_NORESERVE + PROT_NONE version) // brk has been removed - // https://www.dragonflydigest.com/2019/02/22/22586.html + // https://web.archive.org/web/20221006070113/https://www.dragonflydigest.com/2019/02/22/22586.html // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/dc676eaefa61b0f47bbea1c53eab86fd5ccd78c6 // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/4b5665564ef37dc939a3a9ffbafaab9894c18885 // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/8618d94a0e2ff8303ad93c123a3fa598c26a116e @@ -968,7 +968,7 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment) scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); // Assume sbrk returns the old break. Most online documentation confirms - // that, except for http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf, + // that, except for https://web.archive.org/web/20171014020821/http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf, // which claims the returned value is not portable. auto p = sbrk(rounded); if (p == cast(void*) -1) diff --git a/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d index 3770af10ceb33e97be8cef3a11206475df56cfe9..1d1e480ec56fcb5822430c77e0f4e0bc354c4860 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d @@ -134,7 +134,7 @@ enum Options : ulong bytesNotMoved = 1u << 17, /** Measures the sum of extra bytes allocated beyond the bytes requested, i.e. - the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current + the $(HTTPS en.wikipedia.org/wiki/Fragmentation_(computing)#Internal_fragmentation, internal fragmentation). This is the current effective number of slack bytes, and it goes up and down with time. */ bytesSlack = 1u << 18, diff --git a/libphobos/src/std/experimental/allocator/mallocator.d b/libphobos/src/std/experimental/allocator/mallocator.d index 3d4dc9a2c5245918093390dac34521cb28f5cacc..087dbec449a56eb49edbee7c59cd9f7cade663e9 100644 --- a/libphobos/src/std/experimental/allocator/mallocator.d +++ b/libphobos/src/std/experimental/allocator/mallocator.d @@ -52,7 +52,7 @@ struct Mallocator import core.memory : pureRealloc; if (!s) { - // fuzzy area in the C standard, see http://goo.gl/ZpWeSE + // fuzzy area in the C standard, see https://stackoverflow.com/questions/6502077/malloc-and-realloc-functions // so just deallocate and nullify the pointer deallocate(b); b = null; diff --git a/libphobos/src/std/experimental/allocator/mmap_allocator.d b/libphobos/src/std/experimental/allocator/mmap_allocator.d index 4151d0e05048ec99de131c97778523df43699cb2..494d5a3b40a084a2e2e5c697cfe8248a17b7bf0c 100644 --- a/libphobos/src/std/experimental/allocator/mmap_allocator.d +++ b/libphobos/src/std/experimental/allocator/mmap_allocator.d @@ -60,7 +60,7 @@ struct MmapAllocator // http://man7.org/linux/man-pages/man2/mmap.2.html package alias allocateZeroed = allocate; else version (NetBSD) - // http://netbsd.gw.com/cgi-bin/man-cgi?mmap+2+NetBSD-current + // https://man.netbsd.org/mmap.2 package alias allocateZeroed = allocate; else version (Solaris) // https://docs.oracle.com/cd/E88353_01/html/E37841/mmap-2.html diff --git a/libphobos/src/std/format/internal/read.d b/libphobos/src/std/format/internal/read.d index 9130499081c1eeb1c5da7f7928d229671cd34093..597898c9e230baf336822ab90f6729576aae4925 100644 --- a/libphobos/src/std/format/internal/read.d +++ b/libphobos/src/std/format/internal/read.d @@ -161,15 +161,16 @@ if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementT enforceFmt(find(acceptedSpecs!T, spec.spec).length, text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - static if (T.sizeof == 1) + enum int size = T.sizeof; + static if (size == 1) return unformatValue!ubyte(input, spec); - else static if (T.sizeof == 2) + else static if (size == 2) return unformatValue!ushort(input, spec); - else static if (T.sizeof == 4) + else static if (size == 4) return unformatValue!uint(input, spec); else static assert(false, T.stringof ~ ".sizeof must be 1, 2, or 4 not " ~ - to!string(T.sizeof)); + size.stringof); } T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt) diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d index f1d470558360e7d4801504301ac0adbbf31c7211..a78e1b3d0dc806cd42829081a57cfa5cff108249 100644 --- a/libphobos/src/std/format/package.d +++ b/libphobos/src/std/format/package.d @@ -358,7 +358,7 @@ $(BOOKTABLE , Default precision is large enough to add all digits of the integral value. - In case of ($B 'a') and $(B 'A'), the integral digit can be + In case of $(B 'a') and $(B 'A'), the integral digit can be any hexadecimal digit. ) ) diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index 6e94a5d71ce746be9e009628f802d44fa89305cc..9dcec89b2cea71ae359e2bcbed273646ad71ae46 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -13,7 +13,7 @@ also $(LINK https://forum.dlang.org/post/dzfyaxypmkdrpakmycjv@forum.dlang.org).) Copyright: Copyright Jeremie Pelletier 2008 - 2009. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jeremie Pelletier, David Herberth -References: $(LINK http://json.org/), $(LINK http://seriot.ch/parsing_json.html) +References: $(LINK http://json.org/), $(LINK https://seriot.ch/projects/parsing_json.html) Source: $(PHOBOSSRC std/json.d) */ /* @@ -804,7 +804,22 @@ struct JSONValue assert(j["author"].str == "Walter"); } - /// + /** + * Compare two JSONValues for equality + * + * JSON arrays and objects are compared deeply. The order of object keys does not matter. + * + * Floating point numbers are compared for exact equality, not approximal equality. + * + * Different number types (unsigned, signed, and floating) will be compared by converting + * them to a common type, in the same way that comparison of built-in D `int`, `uint` and + * `float` works. + * + * Other than that, types must match exactly. + * Empty arrays are not equal to empty objects, and booleans are never equal to integers. + * + * Returns: whether this `JSONValue` is equal to `rhs` + */ bool opEquals(const JSONValue rhs) const @nogc nothrow pure @safe { return opEquals(rhs); @@ -871,9 +886,13 @@ struct JSONValue /// @safe unittest { - assert(JSONValue(0u) == JSONValue(0)); - assert(JSONValue(0u) == JSONValue(0.0)); - assert(JSONValue(0) == JSONValue(0.0)); + assert(JSONValue(10).opEquals(JSONValue(10.0))); + assert(JSONValue(10) != (JSONValue(10.5))); + + assert(JSONValue(1) != JSONValue(true)); + assert(JSONValue.emptyArray != JSONValue.emptyObject); + + assert(parseJSON(`{"a": 1, "b": 2}`).opEquals(parseJSON(`{"b": 2, "a": 1}`))); } /// Implements the foreach `opApply` interface for json arrays. diff --git a/libphobos/src/std/math/exponential.d b/libphobos/src/std/math/exponential.d index 8fcd88f4c6867969520df67fabe0cd6f244209d3..7a72f428cd72a61285f1daec3f4e0a34f1272814 100644 --- a/libphobos/src/std/math/exponential.d +++ b/libphobos/src/std/math/exponential.d @@ -422,217 +422,7 @@ if (isIntegral!I && isFloatingPoint!F) Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow if (isFloatingPoint!(F) && isFloatingPoint!(G)) { - import core.math : fabs, sqrt; - import std.math.traits : isInfinity, isNaN, signbit; - - alias Float = typeof(return); - - static real impl(real x, real y) @nogc pure nothrow - { - // Special cases. - if (isNaN(y)) - return y; - if (isNaN(x) && y != 0.0) - return x; - - // Even if x is NaN. - if (y == 0.0) - return 1.0; - if (y == 1.0) - return x; - - if (isInfinity(y)) - { - if (isInfinity(x)) - { - if (!signbit(y) && !signbit(x)) - return F.infinity; - else - return F.nan; - } - else if (fabs(x) > 1) - { - if (signbit(y)) - return +0.0; - else - return F.infinity; - } - else if (fabs(x) == 1) - { - return F.nan; - } - else // < 1 - { - if (signbit(y)) - return F.infinity; - else - return +0.0; - } - } - if (isInfinity(x)) - { - if (signbit(x)) - { - long i = cast(long) y; - if (y > 0.0) - { - if (i == y && i & 1) - return -F.infinity; - else if (i == y) - return F.infinity; - else - return -F.nan; - } - else if (y < 0.0) - { - if (i == y && i & 1) - return -0.0; - else if (i == y) - return +0.0; - else - return F.nan; - } - } - else - { - if (y > 0.0) - return F.infinity; - else if (y < 0.0) - return +0.0; - } - } - - if (x == 0.0) - { - if (signbit(x)) - { - long i = cast(long) y; - if (y > 0.0) - { - if (i == y && i & 1) - return -0.0; - else - return +0.0; - } - else if (y < 0.0) - { - if (i == y && i & 1) - return -F.infinity; - else - return F.infinity; - } - } - else - { - if (y > 0.0) - return +0.0; - else if (y < 0.0) - return F.infinity; - } - } - if (x == 1.0) - return 1.0; - - if (y >= F.max) - { - if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0)) - return 0.0; - if (x > 1.0 || x < -1.0) - return F.infinity; - } - if (y <= -F.max) - { - if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0)) - return F.infinity; - if (x > 1.0 || x < -1.0) - return 0.0; - } - - if (x >= F.max) - { - if (y > 0.0) - return F.infinity; - else - return 0.0; - } - if (x <= -F.max) - { - long i = cast(long) y; - if (y > 0.0) - { - if (i == y && i & 1) - return -F.infinity; - else - return F.infinity; - } - else if (y < 0.0) - { - if (i == y && i & 1) - return -0.0; - else - return +0.0; - } - } - - // Integer power of x. - long iy = cast(long) y; - if (iy == y && fabs(y) < 32_768.0) - return pow(x, iy); - - real sign = 1.0; - if (x < 0) - { - // Result is real only if y is an integer - // Check for a non-zero fractional part - enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L; - static if (maxOdd > ulong.max) - { - // Generic method, for any FP type - import std.math.rounding : floor; - if (floor(y) != y) - return sqrt(x); // Complex result -- create a NaN - - const hy = 0.5 * y; - if (floor(hy) != hy) - sign = -1.0; - } - else - { - // Much faster, if ulong has enough precision - const absY = fabs(y); - if (absY <= maxOdd) - { - const uy = cast(ulong) absY; - if (uy != absY) - return sqrt(x); // Complex result -- create a NaN - - if (uy & 1) - sign = -1.0; - } - } - x = -x; - } - version (INLINE_YL2X) - { - // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) ) - // TODO: This is not accurate in practice. A fast and accurate - // (though complicated) method is described in: - // "An efficient rounding boundary test for pow(x, y) - // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). - return sign * exp2( core.math.yl2x(x, y) ); - } - else - { - // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) ) - // TODO: This is not accurate in practice. A fast and accurate - // (though complicated) method is described in: - // "An efficient rounding boundary test for pow(x, y) - // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). - Float w = exp2(y * log2(x)); - return sign * w; - } - } - return impl(x, y); + return _powImpl(x, y); } /// @@ -802,6 +592,216 @@ if (isFloatingPoint!(F) && isFloatingPoint!(G)) assert(pow(-real.infinity, 0.0) == 1.0); } +private real _powImpl(real x, real y) @safe @nogc pure nothrow +{ + alias F = real; + import core.math : fabs, sqrt; + import std.math.traits : isInfinity, isNaN, signbit; + + // Special cases. + if (isNaN(y)) + return y; + if (isNaN(x) && y != 0.0) + return x; + + // Even if x is NaN. + if (y == 0.0) + return 1.0; + if (y == 1.0) + return x; + + if (isInfinity(y)) + { + if (isInfinity(x)) + { + if (!signbit(y) && !signbit(x)) + return F.infinity; + else + return F.nan; + } + else if (fabs(x) > 1) + { + if (signbit(y)) + return +0.0; + else + return F.infinity; + } + else if (fabs(x) == 1) + { + return F.nan; + } + else // < 1 + { + if (signbit(y)) + return F.infinity; + else + return +0.0; + } + } + if (isInfinity(x)) + { + if (signbit(x)) + { + long i = cast(long) y; + if (y > 0.0) + { + if (i == y && i & 1) + return -F.infinity; + else if (i == y) + return F.infinity; + else + return -F.nan; + } + else if (y < 0.0) + { + if (i == y && i & 1) + return -0.0; + else if (i == y) + return +0.0; + else + return F.nan; + } + } + else + { + if (y > 0.0) + return F.infinity; + else if (y < 0.0) + return +0.0; + } + } + + if (x == 0.0) + { + if (signbit(x)) + { + long i = cast(long) y; + if (y > 0.0) + { + if (i == y && i & 1) + return -0.0; + else + return +0.0; + } + else if (y < 0.0) + { + if (i == y && i & 1) + return -F.infinity; + else + return F.infinity; + } + } + else + { + if (y > 0.0) + return +0.0; + else if (y < 0.0) + return F.infinity; + } + } + if (x == 1.0) + return 1.0; + + if (y >= F.max) + { + if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0)) + return 0.0; + if (x > 1.0 || x < -1.0) + return F.infinity; + } + if (y <= -F.max) + { + if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0)) + return F.infinity; + if (x > 1.0 || x < -1.0) + return 0.0; + } + + if (x >= F.max) + { + if (y > 0.0) + return F.infinity; + else + return 0.0; + } + if (x <= -F.max) + { + long i = cast(long) y; + if (y > 0.0) + { + if (i == y && i & 1) + return -F.infinity; + else + return F.infinity; + } + else if (y < 0.0) + { + if (i == y && i & 1) + return -0.0; + else + return +0.0; + } + } + + // Integer power of x. + long iy = cast(long) y; + if (iy == y && fabs(y) < 32_768.0) + return pow(x, iy); + + real sign = 1.0; + if (x < 0) + { + // Result is real only if y is an integer + // Check for a non-zero fractional part + enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L; + static if (maxOdd > ulong.max) + { + // Generic method, for any FP type + import std.math.rounding : floor; + if (floor(y) != y) + return sqrt(x); // Complex result -- create a NaN + + const hy = 0.5 * y; + if (floor(hy) != hy) + sign = -1.0; + } + else + { + // Much faster, if ulong has enough precision + const absY = fabs(y); + if (absY <= maxOdd) + { + const uy = cast(ulong) absY; + if (uy != absY) + return sqrt(x); // Complex result -- create a NaN + + if (uy & 1) + sign = -1.0; + } + } + x = -x; + } + version (INLINE_YL2X) + { + // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) ) + // TODO: This is not accurate in practice. A fast and accurate + // (though complicated) method is described in: + // "An efficient rounding boundary test for pow(x, y) + // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). + return sign * exp2( core.math.yl2x(x, y) ); + } + else + { + // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) ) + // TODO: This is not accurate in practice. A fast and accurate + // (though complicated) method is described in: + // "An efficient rounding boundary test for pow(x, y) + // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). + auto w = exp2(y * log2(x)); + return sign * w; + } +} + /** Computes the value of a positive integer `x`, raised to the power `n`, modulo `m`. * * Params: diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d index 648b70eeea841f0c1a651c29997a4f2f45c61653..9f0fb564de378e28d02fbc8ee52db69779c63728 100644 --- a/libphobos/src/std/numeric.d +++ b/libphobos/src/std/numeric.d @@ -3405,7 +3405,7 @@ private: // This algorithm works by performing the even and odd parts of our FFT // using the "two for the price of one" method mentioned at - // http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM#Head521 + // https://web.archive.org/web/20180312110051/http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM#Head521 // by making the odd terms into the imaginary components of our new FFT, // and then using symmetry to recombine them. void fftImplPureReal(Ret, R)(R range, Ret buf) const diff --git a/libphobos/src/std/outbuffer.d b/libphobos/src/std/outbuffer.d index 92af9a9a16a74865ecf45a91fe7a04568d4efe99..f6d4ba850f30f241e9241d0b5acaf7a487630704 100644 --- a/libphobos/src/std/outbuffer.d +++ b/libphobos/src/std/outbuffer.d @@ -22,9 +22,10 @@ import std.traits : isSomeString; * OutBuffer's byte order is the format native to the computer. * To control the byte order (endianness), use a class derived * from OutBuffer. + * * OutBuffer's internal buffer is allocated with the GC. Pointers * stored into the buffer are scanned by the GC, but you have to - * ensure proper alignment, e.g. by using alignSize((void*).sizeof). + * ensure proper alignment, e.g. by using `alignSize((void*).sizeof)`. */ class OutBuffer @@ -297,7 +298,7 @@ class OutBuffer * Append output of C's vprintf() to internal buffer. */ - void vprintf(scope string format, va_list args) @trusted nothrow + void vprintf(scope string format, va_list args) @system nothrow { import core.stdc.stdio : vsnprintf; import core.stdc.stdlib : alloca; @@ -342,7 +343,7 @@ class OutBuffer * Append output of C's printf() to internal buffer. */ - void printf(scope string format, ...) @trusted + void printf(scope string format, ...) @system { va_list ap; va_start(ap, format); @@ -475,7 +476,7 @@ class OutBuffer buf.write("hello"); buf.write(cast(byte) 0x20); buf.write("world"); - buf.printf(" %d", 62665); + buf.writef(" %d", 62665); assert(cmp(buf.toString(), "hello world 62665") == 0); buf.clear(); diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index fadb4c1759d57abd8f3e2e5f7b49a59be7d63a54..7525d9b1491ce5287ee36c5c902d17bd4a48700d 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -884,11 +884,26 @@ identical to the non-@safe case, but safety introduces some restrictions: */ @trusted auto task(F, Args...)(F fun, Args args) -if (is(typeof(fun(args))) && isSafeTask!F) +if (__traits(compiles, () @safe => fun(args)) && isSafeTask!F) { return new Task!(run, F, Args)(fun, args); } +@safe unittest +{ + static struct Oops { + int convert() { + *cast(int*) 0xcafebabe = 0xdeadbeef; + return 0; + } + alias convert this; + } + static void foo(int) @safe {} + + static assert(!__traits(compiles, task(&foo, Oops.init))); + static assert(!__traits(compiles, scopedTask(&foo, Oops.init))); +} + /** These functions allow the creation of `Task` objects on the stack rather than the GC heap. The lifetime of a `Task` created by `scopedTask` @@ -928,7 +943,7 @@ if (is(typeof(delegateOrFp(args))) && !isSafeTask!F) /// Ditto @trusted auto scopedTask(F, Args...)(F fun, Args args) -if (is(typeof(fun(args))) && isSafeTask!F) +if (__traits(compiles, () @safe => fun(args)) && isSafeTask!F) { auto ret = Task!(run, F, Args)(fun, args); ret.isScoped = true; diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index b6fddf76559bf250ec0a748cfdc7faae165473c2..e2a2d7d2564da78c387974e8896e29e07bc638bf 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -6070,10 +6070,13 @@ nothrow pure @system unittest Generate lockstep's opApply function as a mixin string. If withIndex is true prepend a size_t index to the delegate. */ -private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) +private struct LockstepMixin(Ranges...) { + import std.conv : text; import std.format : format; + string name; + string implName; string[] params; string[] emptyChecks; string[] dgArgs; @@ -6081,76 +6084,101 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) string indexDef; string indexInc; - if (withIndex) +@safe pure: + this(bool withIndex, bool reverse) { - params ~= "size_t"; - dgArgs ~= "index"; - if (reverse) + if (withIndex) { - indexDef = q{ - size_t index = ranges[0].length-1; - enforce(_stoppingPolicy == StoppingPolicy.requireSameLength, - "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); + params ~= "size_t"; + dgArgs ~= "index"; + if (reverse) + { + indexDef = q{ + size_t index = ranges[0].length - 1; + enforce(this.stoppingPolicy == StoppingPolicy.requireSameLength, + "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); - foreach (range; ranges[1..$]) - enforce(range.length == ranges[0].length); - }; - indexInc = "--index;"; + foreach (range; ranges[1 .. $]) + enforce(range.length == ranges[0].length); + }; + indexInc = "--index;"; + } + else + { + indexDef = "size_t index = 0;"; + indexInc = "++index;"; + } } - else + + foreach (idx, Range; Ranges) { - indexDef = "size_t index = 0;"; - indexInc = "++index;"; + params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); + emptyChecks ~= format("!ranges[%s].empty", idx); + if (reverse) + { + dgArgs ~= format("ranges[%s].back", idx); + popFronts ~= format("ranges[%s].popBack();", idx); + } + else + { + dgArgs ~= format("ranges[%s].front", idx); + popFronts ~= format("ranges[%s].popFront();", idx); + } } - } - foreach (idx, Range; Ranges) - { - params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); - emptyChecks ~= format("!ranges[%s].empty", idx); if (reverse) { - dgArgs ~= format("ranges[%s].back", idx); - popFronts ~= format("ranges[%s].popBack();", idx); + name = "opApplyReverse"; + if (withIndex) implName = "opApplyReverseIdxImpl"; + else implName = "opApplyReverseImpl"; } else { - dgArgs ~= format("ranges[%s].front", idx); - popFronts ~= format("ranges[%s].popFront();", idx); + name = "opApply"; + if (withIndex) implName = "opApplyIdxImpl"; + else implName = "opApplyImpl"; } } - string name = reverse ? "opApplyReverse" : "opApply"; - - return format( - q{ - int %s(scope int delegate(%s) dg) - { - import std.exception : enforce; - - auto ranges = _ranges; - int res; - %s +const: + string getAlias() + { + return format(q{ + alias %s = %s!(int delegate(%-(%s%|, %))); + }, + name, implName, params + ); + } - while (%s) + string getImpl() + { + return format(q{ + int %s(DG)(scope DG dg) scope { - res = dg(%s); - if (res) break; - %s + import std.exception : enforce; + + auto ranges = this.ranges; %s - } - if (_stoppingPolicy == StoppingPolicy.requireSameLength) - { - foreach (range; ranges) - enforce(range.empty); + while (%-(%s%| && %)) + { + if (int result = dg(%-(%s%|, %))) return result; + %-(%s%| + %) + %s + } + + if (this.stoppingPolicy == StoppingPolicy.requireSameLength) + { + foreach (range; ranges) + enforce(range.empty); + } + return 0; } - return res; - } - }, name, params.join(", "), indexDef, - emptyChecks.join(" && "), dgArgs.join(", "), - popFronts.join("\n "), - indexInc); + }, + implName, indexDef, emptyChecks, dgArgs, popFronts, indexInc + ); + } } /** @@ -6170,10 +6198,6 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) By default `StoppingPolicy` is set to `StoppingPolicy.shortest`. - Limitations: The `pure`, `@safe`, `@nogc`, or `nothrow` attributes cannot be - inferred for `lockstep` iteration. $(LREF zip) can infer the first two due to - a different implementation. - See_Also: $(LREF zip) `lockstep` is similar to $(LREF zip), but `zip` bundles its @@ -6184,41 +6208,53 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) struct Lockstep(Ranges...) if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) { + private Ranges ranges; + private StoppingPolicy stoppingPolicy; + /// - this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest) + this(Ranges ranges, StoppingPolicy sp = StoppingPolicy.shortest) { import std.exception : enforce; - _ranges = ranges; + this.ranges = ranges; enforce(sp != StoppingPolicy.longest, - "Can't use StoppingPolicy.Longest on Lockstep."); - _stoppingPolicy = sp; + "Can't use StoppingPolicy.Longest on Lockstep."); + this.stoppingPolicy = sp; } - mixin(lockstepMixin!Ranges(false, false)); - mixin(lockstepMixin!Ranges(true, false)); + private enum lockstepMixinFF = LockstepMixin!Ranges(withIndex: false, reverse: false); + mixin(lockstepMixinFF.getImpl); + + private enum lockstepMixinTF = LockstepMixin!Ranges(withIndex: true, reverse: false); + mixin(lockstepMixinTF.getImpl); + + mixin(lockstepMixinFF.getAlias); + mixin(lockstepMixinTF.getAlias); + static if (allSatisfy!(isBidirectionalRange, Ranges)) { - mixin(lockstepMixin!Ranges(false, true)); + private enum lockstepMixinFT = LockstepMixin!Ranges(withIndex: false, reverse: true); + mixin(lockstepMixinFT.getImpl); static if (allSatisfy!(hasLength, Ranges)) { - mixin(lockstepMixin!Ranges(true, true)); + private enum lockstepMixinTT = LockstepMixin!Ranges(withIndex: true, reverse: true); + mixin(lockstepMixinTT.getImpl); + mixin(lockstepMixinTT.getAlias); } else { - mixin(lockstepReverseFailMixin!Ranges(true)); + mixin(lockstepReverseFailMixin!Ranges(withIndex: true)); + alias opApplyReverse = opApplyReverseIdxFail; } + mixin(lockstepMixinFT.getAlias); } else { - mixin(lockstepReverseFailMixin!Ranges(false)); - mixin(lockstepReverseFailMixin!Ranges(true)); + mixin(lockstepReverseFailMixin!Ranges(withIndex: false)); + mixin(lockstepReverseFailMixin!Ranges(withIndex: true)); + alias opApplyReverse = opApplyReverseFail; + alias opApplyReverse = opApplyReverseIdxFail; } - -private: - alias R = Ranges; - R _ranges; - StoppingPolicy _stoppingPolicy; } /// Ditto @@ -6238,33 +6274,39 @@ if (allSatisfy!(isInputRange, Ranges)) } /// -@system unittest +pure @safe unittest { - auto arr1 = [1,2,3,4,5,100]; - auto arr2 = [6,7,8,9,10]; + int[6] arr1 = [1,2,3,4,5,100]; + int[5] arr2 = [6,7,8,9,10]; - foreach (ref a, b; lockstep(arr1, arr2)) - { - a += b; - } + foreach (ref a, b; lockstep(arr1[], arr2[])) + { + a += b; + } - assert(arr1 == [7,9,11,13,15,100]); + assert(arr1 == [7,9,11,13,15,100]); +} - /// Lockstep also supports iterating with an index variable: - foreach (index, a, b; lockstep(arr1, arr2)) - { - assert(arr1[index] == a); - assert(arr2[index] == b); - } +/// Lockstep also supports iterating with an index variable: +pure @safe unittest +{ + int[3] arr1 = [1,2,3]; + int[3] arr2 = [4,5,6]; + + foreach (index, a, b; lockstep(arr1[], arr2[])) + { + assert(arr1[index] == a); + assert(arr2[index] == b); + } } // https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep -@system unittest +pure @safe unittest { auto arr1 = [0, 1, 2, 3]; auto arr2 = [4, 5, 6, 7]; - size_t n = arr1.length -1; + size_t n = arr1.length - 1; foreach_reverse (index, a, b; lockstep(arr1, arr2, StoppingPolicy.requireSameLength)) { assert(n == index); @@ -6283,7 +6325,7 @@ if (allSatisfy!(isInputRange, Ranges)) } } -@system unittest +pure @safe unittest { import std.algorithm.iteration : filter; import std.conv : to; @@ -6380,7 +6422,7 @@ if (allSatisfy!(isInputRange, Ranges)) foreach (x, y; lockstep(iota(0, 10), iota(0, 10))) { } } -@system unittest +pure @safe unittest { struct RvalueRange { @@ -6436,11 +6478,11 @@ private string lockstepReverseFailMixin(Ranges...)(bool withIndex) return format( q{ - int opApplyReverse()(scope int delegate(%s) dg) + int opApplyReverse%sFail()(scope int delegate(%s) dg) { static assert(false, "%s"); } - }, params.join(", "), message); + }, withIndex ? "Idx" : "" , params.join(", "), message); } // For generic programming, make sure Lockstep!(Range) is well defined for a diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index 8caa9b3671870d3d5f06c1c97631757753a8cc44..b4744600cd5958392de1ace9e21b4346ae0b382c 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -220,31 +220,6 @@ version (CRuntime_Microsoft) private alias _FGETWC = _fgetwc_nolock; private alias _FLOCK = _lock_file; private alias _FUNLOCK = _unlock_file; - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = _fputc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = _fputwc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = _fgetc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = _fgetwc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = _lock_file; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = _unlock_file; } else version (CRuntime_Glibc) { @@ -254,31 +229,6 @@ else version (CRuntime_Glibc) private alias _FGETWC = fgetwc_unlocked; private alias _FLOCK = core.sys.posix.stdio.flockfile; private alias _FUNLOCK = core.sys.posix.stdio.funlockfile; - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = fputc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = fputwc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = fgetc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = fgetwc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = core.sys.posix.stdio.flockfile; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = core.sys.posix.stdio.funlockfile; } else version (GENERIC_IO) { @@ -304,52 +254,6 @@ else version (GENERIC_IO) { static assert(0, "don't know how to lock files on GENERIC_IO"); } - - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fputc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, fputc.mangleof) int fputc_unlocked(int c, _iobuf* fp); - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fputwc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int fputwc_unlocked(wchar_t c, _iobuf* fp); - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fgetc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, fgetc.mangleof) int fgetc_unlocked(_iobuf* fp); - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fgetwc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int fgetwc_unlocked(_iobuf* fp); - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = fputc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = fputwc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = fgetc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = fgetwc_unlocked; - - version (Posix) - { - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = core.sys.posix.stdio.flockfile; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = core.sys.posix.stdio.funlockfile; - } } else { @@ -795,7 +699,7 @@ Throws: `ErrnoException` in case of error. /** Detaches from the current file (throwing on failure), and then runs a command by calling the C standard library function $(HTTP -opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen). +pubs.opengroup.org/onlinepubs/7908799/xsh/popen.html, popen). Throws: `ErrnoException` in case of error. */ @@ -813,8 +717,9 @@ The mode must be compatible with the mode of the file descriptor. Throws: `ErrnoException` in case of error. Params: fd = File descriptor to associate with this `File`. - stdioOpenmode = Mode to associate with this File. The mode has the same semantics - semantics as in the C standard library $(CSTDIO fdopen) function, + stdioOpenmode = Mode to associate with this File. The mode has the same + semantics as in the POSIX library function $(HTTP + pubs.opengroup.org/onlinepubs/7908799/xsh/fdopen.html, fdopen) and must be compatible with `fd`. */ void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe @@ -1135,6 +1040,9 @@ Throws: `ErrnoException` if the file is not opened or the call to `fread` fails. assert(buf == "\r\n\n\r\n"); } + // https://issues.dlang.org/show_bug.cgi?id=24685 + static assert(!__traits(compiles, (File f) @safe { int*[1] bar; f.rawRead(bar[]); })); + // https://issues.dlang.org/show_bug.cgi?id=21729 @system unittest { @@ -4576,11 +4484,11 @@ if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) && { /* * The new opengroup large file support API is transparently - * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0 + * included in the normal C bindings. https://www.opengroup.org/platform/lfs.html#1.0 * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and * the normal functions work fine. If not, then large file support * probably isn't available. Do not use the old transitional API - * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0) + * (the native extern(C) fopen64, https://unix.org/version2/whatsnew/lfs20mar.html#3.0) */ import core.sys.posix.stdio : fopen; return fopen(namez, modez); @@ -4629,6 +4537,13 @@ private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted * Convenience function that forwards to `core.stdc.stdio.fread` */ private auto trustedFread(T)(FILE* f, T[] obj) @trusted +if (!imported!"std.traits".hasIndirections!T) +{ + return fread(obj.ptr, T.sizeof, obj.length, f); +} + +private auto trustedFread(T)(FILE* f, T[] obj) @system +if (imported!"std.traits".hasIndirections!T) { return fread(obj.ptr, T.sizeof, obj.length, f); } diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index 3c425c7d7de08f801f983da6a42594775b1775fa..aceb2878d49cc76eb24656408cb2c20fd364d4fd 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -3543,6 +3543,35 @@ struct Nullable(T) format!"%s"(a); } + /** + * Returns true if `this` has a value, otherwise false. + * + * Allows a `Nullable` to be used as the condition in an `if` statement: + * + * --- + * if (auto result = functionReturningNullable()) + * { + * doSomethingWith(result.get); + * } + * --- + */ + bool opCast(T : bool)() const + { + return !isNull; + } + + /// Prevents `opCast` from disabling built-in conversions. + auto ref T opCast(T, this This)() + if (is(This : T) || This.sizeof == T.sizeof) + { + static if (is(This : T)) + // Convert implicitly + return this; + else + // Reinterpret + return *cast(T*) &this; + } + /** * Forces `this` to the null state. */ @@ -4400,6 +4429,26 @@ auto nullable(T)(T t) assert(destroyed); } +// https://issues.dlang.org/show_bug.cgi?id=22293 +@safe unittest +{ + Nullable!int empty; + Nullable!int full = 123; + + assert(cast(bool) empty == false); + assert(cast(bool) full == true); + + if (empty) assert(0); + if (!full) assert(0); +} + +// check that opCast doesn't break unsafe casts +@system unittest +{ + Nullable!(const(int*)) a; + auto result = cast(immutable(Nullable!(int*))) a; +} + /** Just like `Nullable!T`, except that the null state is defined as a particular value. For example, $(D Nullable!(uint, uint.max)) is an @@ -10928,19 +10977,22 @@ struct RefCounted(T, RefCountedAutoInitialize autoInit = swap(_refCounted._store, rhs._refCounted._store); } - void opAssign(T rhs) + static if (__traits(compiles, lvalueOf!T = T.init)) { - import std.algorithm.mutation : move; - - static if (autoInit == RefCountedAutoInitialize.yes) + void opAssign(T rhs) { - _refCounted.ensureInitialized(); - } - else - { - assert(_refCounted.isInitialized); + import std.algorithm.mutation : move; + + static if (autoInit == RefCountedAutoInitialize.yes) + { + _refCounted.ensureInitialized(); + } + else + { + assert(_refCounted.isInitialized); + } + move(rhs, _refCounted._store._payload); } - move(rhs, _refCounted._store._payload); } static if (autoInit == RefCountedAutoInitialize.yes) diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d index f0d5d4d268bec6ad7b916d14b4e26a97ea87ac3d..9a326a5ac3aff8ed471f0fbce3388b7f9c0e591c 100644 --- a/libphobos/src/std/utf.d +++ b/libphobos/src/std/utf.d @@ -53,7 +53,7 @@ $(TR $(TD Miscellaneous) $(TD See_Also: $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br> $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br> - $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335) + $(LINK https://web.archive.org/web/20100113043530/https://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335) Copyright: Copyright The D Language Foundation 2000 - 2012. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP digitalmars.com, Walter Bright) and