diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-05 14:56:59 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-10 22:09:00 +0100 |
commit | 89629b271827357f81f6f8c7cf28f59919519864 (patch) | |
tree | bc6ae2e8d764a128cd9501b30ad96ce7e6360e85 /gcc/d/dmd | |
parent | c9353e0fcd0ddc0d48ae8a2b0518f0f82670d708 (diff) | |
download | gcc-89629b271827357f81f6f8c7cf28f59919519864.zip gcc-89629b271827357f81f6f8c7cf28f59919519864.tar.gz gcc-89629b271827357f81f6f8c7cf28f59919519864.tar.bz2 |
d: Merge dmd, druntime 34875cd6e1, phobos ebd24da8a
D front-end changes:
- Import dmd v2.110.0-beta.1.
- `ref' can now be applied to local, static, extern, and global
variables.
D runtime changes:
- Import druntime v2.110.0-beta.1.
Phobos changes:
- Import phobos v2.110.0-beta.1.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd 34875cd6e1.
* dmd/VERSION: Bump version to v2.110.0-beta.1.
* Make-lang.in (D_FRONTEND_OBJS): Add d/deps.o, d/timetrace.o.
* decl.cc (class DeclVisitor): Update for new front-end interface.
* expr.cc (class ExprVisitor): Likewise
* typeinfo.cc (check_typeinfo_type): Likewise.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime 34875cd6e1.
* src/MERGE: Merge upstream phobos ebd24da8a.
Diffstat (limited to 'gcc/d/dmd')
55 files changed, 1517 insertions, 1622 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index d458bea..a6072c4 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 0787594..00bbf84 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 ffc1c3e..eef25e2 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 8d02203..f29755b 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 e30160d..5fe5273 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 1c8eb57..db14f9a 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 31155f1..c96a65b 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 17b33d8..4ebe332 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 01aa56d..926cdf8 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 0609778..2991545 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 0b3096d..09e364d 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 2905967..cda50cd 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 2199d5d..74074d7 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 a1202ed..d35857b 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 8f706fb..4ac22b9 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 62800d3..95064b0 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 0000000..efb4b0d --- /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 52520be..0f900c9 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 c04fbec..9d70250 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 005f1c9..583b467 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 4719529..7936086 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 90b2d9f..f5ec1ce 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 ce29073..2c5f0e5 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 e32f5fa..711ab12 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 8fcbbad..6883180 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 c67ac61..e1eadc5 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 08bd6fd..fd2bd00 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) { @@ -2277,23 +2018,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 a69b64d..3d2ad47 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 54ff826..c815c58 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 594e481..5d76cea 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 c97aeb6..2bdc185 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 bd28d7b..e2aa82a 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 a44fb28..7f2b225 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 ece56c8d..0413df0 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 8faad30..f48b3c6 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 26a56c2..2a11f30 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 6a2d349..41a645e 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 11c27c6..2ec4bee 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 9e45e45..02f47f8 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 756caf8..ed73233 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 0d32d7d..22fbbc4 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 1fbe0ae..fb9a18b 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 c6986c0..8eaf609 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 f1bd6c9..b0eb3d1 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 f36a14b..7983a7a 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 06a5eef..fb7373c 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 963fa92..89c97cf 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 57a6639..4bcfb18 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 c5c8a12..3b96ecf 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 d26e35d..afd0add 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 0000000..7d1fd73 --- /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 5ec3844..c8608fa 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 33d825a..e4d7a95 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 a319832..5d7154a 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 9228ba6..cde029d 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) { @@ -146,78 +146,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. * * Params: |