diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 702718542275..5aced4b514b7 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -481,7 +481,6 @@ struct ASTBase enum AdrOnStackNone = ~0u; uint ctfeAdrOnStack; uint sequenceNumber; - __gshared uint nextSequenceNumber; final extern (D) this(const ref Loc loc, Type type, Identifier id, Initializer _init, StorageClass st = STC.undefined_) { @@ -490,7 +489,6 @@ struct ASTBase this._init = _init; this.loc = loc; this.storage_class = st; - sequenceNumber = ++nextSequenceNumber; ctfeAdrOnStack = AdrOnStackNone; } diff --git a/src/dmd/declaration.d b/src/dmd/declaration.d index aa143d4369f4..494b60f04937 100644 --- a/src/dmd/declaration.d +++ b/src/dmd/declaration.d @@ -1048,7 +1048,6 @@ extern (C++) class VarDeclaration : Declaration uint endlinnum; // line number of end of scope that this var lives in uint offset; uint sequenceNumber; // order the variables are declared - __gshared uint nextSequenceNumber; // the counter for sequenceNumber structalign_t alignment; // When interpreting, these point to the value (NULL if value not determinable) @@ -1099,7 +1098,6 @@ extern (C++) class VarDeclaration : Declaration this._init = _init; ctfeAdrOnStack = AdrOnStackNone; this.storage_class = storage_class; - sequenceNumber = ++nextSequenceNumber; } static VarDeclaration create(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) @@ -1657,12 +1655,10 @@ extern (C++) class VarDeclaration : Declaration // Sequence numbers work when there are no special VarDeclaration's involved if (!((this.storage_class | v.storage_class) & special)) { - // FIXME: VarDeclaration's for parameters are created in semantic3, so - // they will have a greater sequence number than local variables. - // Hence reverse the result for mixed comparisons. - const exp = this.isParameter() == v.isParameter(); + assert(this.sequenceNumber != this.sequenceNumber.init); + assert(v.sequenceNumber != v.sequenceNumber.init); - return (this.sequenceNumber < v.sequenceNumber) == exp; + return (this.sequenceNumber < v.sequenceNumber); } // Assume that semantic produces temporaries according to their lifetime diff --git a/src/dmd/dsymbolsem.d b/src/dmd/dsymbolsem.d index 387dfe330988..28d1478eafe8 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -356,6 +356,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (sc && sc.inunion && sc.inunion.isAnonDeclaration()) dsym.overlapped = true; + dsym.sequenceNumber = global.varSequenceNumber++; + Scope* scx = null; if (dsym._scope) { diff --git a/src/dmd/escape.d b/src/dmd/escape.d index e60ee9c8f543..b4cdaebb3a54 100644 --- a/src/dmd/escape.d +++ b/src/dmd/escape.d @@ -691,8 +691,8 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) } // If va's lifetime encloses v's, then error - if (va && - (va.enclosesLifetimeOf(v) && !(v.storage_class & (STC.parameter | STC.temp)) || + if (va && !va.isDataseg() && + (va.enclosesLifetimeOf(v) && !(v.storage_class & STC.temp) || // va is class reference ae.e1.isDotVarExp() && va.type.toBasetype().isTypeClass() && (va.enclosesLifetimeOf(v) || !va.isScope()) || diff --git a/src/dmd/frontend.h b/src/dmd/frontend.h index 920c6ab8e611..898ebd01a45a 100644 --- a/src/dmd/frontend.h +++ b/src/dmd/frontend.h @@ -857,6 +857,7 @@ struct Global final Array* versionids; Array* debugids; bool hasMainFunction; + uint32_t varSequenceNumber; enum : int32_t { recursionLimit = 500 }; uint32_t startGagging(); @@ -881,10 +882,11 @@ struct Global final console(), versionids(), debugids(), - hasMainFunction() + hasMainFunction(), + varSequenceNumber(1u) { } - Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, 0u, false, false, (DiagnosticReporting)1u, false, false, false, (FeatureState)-1, (FeatureState)-1, false, false, false, (DiagnosticReporting)2u, (PIC)0u, false, false, 0u, false, false, false, true, true, true, false, false, false, false, false, false, true, false, false, false, (FeatureState)-1, false, false, (CppStdRevision)201103u, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKACTION)0u, 20u, {}, Array(0LLU, {}, arrayliteral), nullptr, nullptr, {}, {}, {}, false, {}, {}, Array(0LLU, {}, arrayliteral), false, {}, {}, true, (CxxHeaderMode)0u, {}, {}, false, {}, (JsonFieldFlags)0u, nullptr, nullptr, 0, 0u, nullptr, 0u, nullptr, {}, {}, {}, {}, nullptr, false, {}, Array(0LLU, {}, arrayliteral), (MessageStyle)0u, false, Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), {}, {}, {}, {}), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false) : + Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, 0u, false, false, (DiagnosticReporting)1u, false, false, false, (FeatureState)-1, (FeatureState)-1, false, false, false, (DiagnosticReporting)2u, (PIC)0u, false, false, 0u, false, false, false, true, true, true, false, false, false, false, false, false, true, false, false, false, (FeatureState)-1, false, false, (CppStdRevision)201103u, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKENABLE)0u, (CHECKACTION)0u, 20u, {}, Array(0LLU, {}, arrayliteral), nullptr, nullptr, {}, {}, {}, false, {}, {}, Array(0LLU, {}, arrayliteral), false, {}, {}, true, (CxxHeaderMode)0u, {}, {}, false, {}, (JsonFieldFlags)0u, nullptr, nullptr, 0, 0u, nullptr, 0u, nullptr, {}, {}, {}, {}, nullptr, false, {}, Array(0LLU, {}, arrayliteral), (MessageStyle)0u, false, Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), Array(0LLU, {}, arrayliteral), {}, {}, {}, {}), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u) : inifilename(inifilename), copyright(copyright), written(written), @@ -900,7 +902,8 @@ struct Global final console(console), versionids(versionids), debugids(debugids), - hasMainFunction(hasMainFunction) + hasMainFunction(hasMainFunction), + varSequenceNumber(varSequenceNumber) {} }; @@ -5772,7 +5775,6 @@ class VarDeclaration : public Declaration uint32_t endlinnum; uint32_t offset; uint32_t sequenceNumber; - static uint32_t nextSequenceNumber; structalign_t alignment; enum : uint32_t { AdrOnStackNone = 4294967295u }; diff --git a/src/dmd/globals.d b/src/dmd/globals.d index f707f2868dcf..eccd1ee183df 100644 --- a/src/dmd/globals.d +++ b/src/dmd/globals.d @@ -327,6 +327,7 @@ extern (C++) struct Global Array!Identifier* debugids; /// command line debug versions and predefined versions bool hasMainFunction; /// Whether a main function has already been compiled in (for -main switch) + uint varSequenceNumber = 1; /// Relative lifetime of `VarDeclaration` within a function, used for `scope` checks enum recursionLimit = 500; /// number of recursive template expansions before abort diff --git a/src/dmd/globals.h b/src/dmd/globals.h index 4e76967ee224..49fa5b98e954 100644 --- a/src/dmd/globals.h +++ b/src/dmd/globals.h @@ -290,6 +290,7 @@ struct Global Array* debugids; // command line debug versions and predefined versions bool hasMainFunction; + unsigned varSequenceNumber; /* Start gagging. Return the current number of gagged errors */ diff --git a/test/fail_compilation/test22298.d b/test/fail_compilation/test22298.d new file mode 100644 index 000000000000..cdb1a3eb5083 --- /dev/null +++ b/test/fail_compilation/test22298.d @@ -0,0 +1,30 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test22298.d(18): Error: scope variable `i` assigned to `p` with longer lifetime +fail_compilation/test22298.d(29): Error: scope variable `y` assigned to `x` with longer lifetime +--- +*/ + +void g(scope void delegate(scope int*) @safe cb) @safe { + int x = 42; + cb(&x); +} + +void main() @safe { + int* p; + void f(scope int* i) @safe { + p = i; + } + + g(&f); + // address of x has escaped g + assert(*p == 42); +} + +void f() @safe { + mixin("scope int* x;"); + scope int* y; + x = y; +}