diff --git a/src/dmd/ctorflow.d b/src/dmd/ctorflow.d index 870d08b32e43..6b10d3f1cd9a 100644 --- a/src/dmd/ctorflow.d +++ b/src/dmd/ctorflow.d @@ -17,6 +17,7 @@ module dmd.ctorflow; import core.stdc.stdio; import dmd.root.rmem; +import dmd.globals : Loc; enum CSX : ushort { @@ -30,6 +31,13 @@ enum CSX : ushort deprecate_18719 = 0x40, // issue deprecation for Issue 18719 - delete when deprecation period is over } +/// Individual field in the Ctor with information about its callees and location. +struct FieldInit +{ + CSX csx; /// information about the field's callees + Loc loc; /// location of the field initialization +} + /*********** * Primitive flow analysis for constructors */ @@ -37,30 +45,19 @@ struct CtorFlow { CSX callSuper; /// state of calling other constructors - CSX[] fieldinit; /// state of field initializations + FieldInit[] fieldinit; /// state of field initializations void allocFieldinit(size_t dim) { - fieldinit = (cast(CSX*)mem.xcalloc(CSX.sizeof, dim))[0 .. dim]; + fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim]; } void freeFieldinit() { if (fieldinit.ptr) mem.xfree(fieldinit.ptr); - fieldinit = null; - } - CSX[] saveFieldInit() - { - CSX[] fi = null; - if (fieldinit.length) // copy - { - const dim = fieldinit.length; - fi = (cast(CSX*)mem.xmalloc(CSX.sizeof * dim))[0 .. dim]; - fi[] = fieldinit[]; - } - return fi; + fieldinit = null; } /*********************** @@ -70,7 +67,7 @@ struct CtorFlow */ CtorFlow clone() { - return CtorFlow(callSuper, saveFieldInit()); + return CtorFlow(callSuper, fieldinit.arraydup); } /********************************** @@ -82,7 +79,7 @@ struct CtorFlow { callSuper |= csx; foreach (ref u; fieldinit) - u |= csx; + u.csx |= csx; } /****************************** @@ -97,7 +94,12 @@ struct CtorFlow { assert(fieldinit.length == ctorflow.fieldinit.length); foreach (i, u; ctorflow.fieldinit) - fieldinit[i] |= u; + { + auto fi = &fieldinit[i]; + fi.csx |= u.csx; + if (fi.loc == Loc.init) + fi.loc = u.loc; + } } } } diff --git a/src/dmd/declaration.d b/src/dmd/declaration.d index 5436accf29cd..4b33dbb57b01 100644 --- a/src/dmd/declaration.d +++ b/src/dmd/declaration.d @@ -12,6 +12,7 @@ module dmd.declaration; +import core.stdc.stdio; import dmd.aggregate; import dmd.arraytypes; import dmd.ctorflow; @@ -112,7 +113,8 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) break; } assert(i < dim); - const fi = sc.ctorflow.fieldinit[i]; + auto fieldInit = &sc.ctorflow.fieldinit[i]; + const fi = fieldInit.csx; if (fi & CSX.this_ctor) { @@ -127,9 +129,14 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) // dmd.ctorflow.CSX enum. // @@@DEPRECATED_2019-01@@@. if (fi & CSX.deprecate_18719) - .deprecation(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + { + .deprecation(loc, "%s field `%s` was initialized in a previous constructor call", modStr, var.toChars()); + } else + { .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + .errorSupplemental(fieldInit.loc, "Previous initialization is here."); + } } } else if (sc.inLoop || (fi & CSX.label)) @@ -143,7 +150,8 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) } } - sc.ctorflow.fieldinit[i] |= CSX.this_ctor; + fieldInit.csx |= CSX.this_ctor; + fieldInit.loc = e1.loc; if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 { foreach (j, v; ad.fields) @@ -151,7 +159,7 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) if (v is var || !var.isOverlappedWith(v)) continue; v.ctorinit = true; - sc.ctorflow.fieldinit[j] = CSX.this_ctor; + sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; } } } diff --git a/src/dmd/dscope.d b/src/dmd/dscope.d index 45f3ccc0f98d..edf9ac0cec82 100644 --- a/src/dmd/dscope.d +++ b/src/dmd/dscope.d @@ -191,7 +191,7 @@ struct Scope } s.slabel = null; s.nofree = false; - s.ctorflow.fieldinit = ctorflow.saveFieldInit(); + s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; s.flags = (flags & SCOPEpush); s.lastdc = null; assert(&this != s); @@ -290,7 +290,7 @@ struct Scope foreach (i, v; ad.fields) { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (!mergeFieldInit(this.ctorflow.fieldinit[i], fies[i]) && mustInit) + if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fies[i].csx) && mustInit) { error(loc, "one path skips field `%s`", v.toChars()); } diff --git a/src/dmd/expression.d b/src/dmd/expression.d index 73fc31a681db..97db7b867f8f 100644 --- a/src/dmd/expression.d +++ b/src/dmd/expression.d @@ -5429,7 +5429,7 @@ extern (C++) final class DotVarExp : UnaExp { if (f == vd) { - if (!(sc.ctorflow.fieldinit[i] & CSX.this_ctor)) + if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor)) { /* If the address of vd is taken, assume it is thereby initialized * https://issues.dlang.org/show_bug.cgi?id=15869 diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index 1b57dc9890ee..efeb24749480 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -3290,7 +3290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // initialized after this call. foreach (ref field; sc.ctorflow.fieldinit) { - field |= CSX.this_ctor | CSX.deprecate_18719; + field.csx |= CSX.this_ctor | CSX.deprecate_18719; } } diff --git a/src/dmd/globals.d b/src/dmd/globals.d index 298301c7797a..4d4a4481507c 100644 --- a/src/dmd/globals.d +++ b/src/dmd/globals.d @@ -432,7 +432,7 @@ struct Loc static immutable Loc initial; /// use for default initialization of const ref Loc's nothrow: - extern (D) this(const(char)* filename, uint linnum, uint charnum) + extern (D) this(const(char)* filename, uint linnum, uint charnum) pure { this.linnum = linnum; this.charnum = charnum; diff --git a/src/dmd/root/rmem.d b/src/dmd/root/rmem.d index 9b4cd3dfc3b7..9ce3ffce4a12 100644 --- a/src/dmd/root/rmem.d +++ b/src/dmd/root/rmem.d @@ -242,18 +242,64 @@ else } } } +/** +Makes a null-terminated copy of the given string on newly allocated memory. +The null-terminator won't be part of the returned string slice. It will be +at position `n` where `n` is the length of the input string. + +Params: + s = string to copy -extern (D) static char[] xarraydup(const(char)[] s) nothrow +Returns: A null-terminated copy of the input array. +*/ +extern (D) char[] xarraydup(const(char)[] s) nothrow { - if (s) - { - auto p = cast(char*)mem.xmalloc(s.length + 1); - char[] a = p[0 .. s.length]; - a[] = s[0 .. s.length]; - p[s.length] = 0; // preserve 0 terminator semantics - return a; - } - return null; + if (!s) + return null; + + auto p = cast(char*)mem.xmalloc(s.length + 1); + char[] a = p[0 .. s.length]; + a[] = s[0 .. s.length]; + p[s.length] = 0; // preserve 0 terminator semantics + return a; } +/// +unittest +{ + auto s1 = "foo"; + auto s2 = s1.xarraydup; + s2[0] = 'b'; + assert(s1 == "foo"); + assert(s2 == "boo"); + assert(*(s2.ptr + s2.length) == '\0'); +} + +/** +Makes a copy of the given array on newly allocated memory. +Params: + s = array to copy + +Returns: A copy of the input array. +*/ +extern (D) T[] arraydup(T)(const scope T[] s) nothrow +{ + if (!s) + return null; + + const dim = s.length; + auto p = (cast(T*)mem.xmalloc(T.sizeof * dim))[0 .. dim]; + p[] = s; + return p; +} + +/// +unittest +{ + auto s1 = [0, 1, 2]; + auto s2 = s1.arraydup; + s2[0] = 4; + assert(s1 == [0, 1, 2]); + assert(s2 == [4, 1, 2]); +} diff --git a/src/dmd/semantic3.d b/src/dmd/semantic3.d index 6f9ea01c8b36..3128a8af576a 100644 --- a/src/dmd/semantic3.d +++ b/src/dmd/semantic3.d @@ -673,7 +673,7 @@ private extern(C++) final class Semantic3Visitor : Visitor else { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (mustInit && !(sc2.ctorflow.fieldinit[i] & CSX.this_ctor)) + if (mustInit && !(sc2.ctorflow.fieldinit[i].csx & CSX.this_ctor)) { funcdecl.error("field `%s` must be initialized but skipped", v.toChars()); } diff --git a/src/dmd/statementsem.d b/src/dmd/statementsem.d index dbf282a7b9ec..01790f61e26d 100644 --- a/src/dmd/statementsem.d +++ b/src/dmd/statementsem.d @@ -3236,7 +3236,7 @@ else foreach (i, v; ad.fields) { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (mustInit && !(sc.ctorflow.fieldinit[i] & CSX.this_ctor)) + if (mustInit && !(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor)) { rs.error("an earlier `return` statement skips field `%s` initialization", v.toChars()); errors = true; diff --git a/test/fail_compilation/diag12678.d b/test/fail_compilation/diag12678.d index bfbfbd58d66c..8b17968fdbdd 100644 --- a/test/fail_compilation/diag12678.d +++ b/test/fail_compilation/diag12678.d @@ -1,9 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/diag12678.d(19): Error: const field `cf1` initialized multiple times -fail_compilation/diag12678.d(22): Error: immutable field `if1` initialized multiple times -fail_compilation/diag12678.d(25): Error: const field `cf2` initialization is not allowed in loops or after labels +fail_compilation/diag12678.d(21): Error: const field `cf1` initialized multiple times +fail_compilation/diag12678.d(20): Previous initialization is here. +fail_compilation/diag12678.d(24): Error: immutable field `if1` initialized multiple times +fail_compilation/diag12678.d(23): Previous initialization is here. +fail_compilation/diag12678.d(27): Error: const field `cf2` initialization is not allowed in loops or after labels --- */ diff --git a/test/fail_compilation/diag19022.d b/test/fail_compilation/diag19022.d new file mode 100644 index 000000000000..0aa26f537947 --- /dev/null +++ b/test/fail_compilation/diag19022.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag19022.d(16): Error: immutable field `b` initialized multiple times +fail_compilation/diag19022.d(15): Previous initialization is here. +--- +*/ +// https://issues.dlang.org/show_bug.cgi?id=19022 + +struct Foo +{ + immutable int b; + this(int a) + { + b = 2; + b = 2; + } +} diff --git a/test/fail_compilation/fail18719.d b/test/fail_compilation/fail18719.d index 9976d4462d28..7bf7e3b5f949 100644 --- a/test/fail_compilation/fail18719.d +++ b/test/fail_compilation/fail18719.d @@ -4,7 +4,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail18719.d(29): Deprecation: immutable field `x` initialized multiple times +fail_compilation/fail18719.d(29): Deprecation: immutable field `x` was initialized in a previous constructor call --- */ diff --git a/test/fail_compilation/fail9665a.d b/test/fail_compilation/fail9665a.d index 54ff6779a353..496c1331373b 100644 --- a/test/fail_compilation/fail9665a.d +++ b/test/fail_compilation/fail9665a.d @@ -1,15 +1,41 @@ // REQUIRED_ARGS: // PERMUTE_ARGS: - -/***************************************************/ -// immutable field - /+ TEST_OUTPUT: --- -fail_compilation/fail9665a.d(19): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(45): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(44): Previous initialization is here. +fail_compilation/fail9665a.d(55): Error: immutable field `v` initialized multiple times + Previous initialization is here. +fail_compilation/fail9665a.d(60): Error: immutable field `v` initialized multiple times + Previous initialization is here. +fail_compilation/fail9665a.d(65): Error: immutable field `v` initialized multiple times + Previous initialization is here. +fail_compilation/fail9665a.d(75): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(74): Previous initialization is here. +fail_compilation/fail9665a.d(80): Error: immutable field `v` initialized multiple times + Previous initialization is here. +fail_compilation/fail9665a.d(85): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(84): Previous initialization is here. +fail_compilation/fail9665a.d(98): Error: immutable field `v` initialization is not allowed in loops or after labels +fail_compilation/fail9665a.d(103): Error: immutable field `v` initialization is not allowed in loops or after labels +fail_compilation/fail9665a.d(108): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(107): Previous initialization is here. +fail_compilation/fail9665a.d(113): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(112): Previous initialization is here. +fail_compilation/fail9665a.d(118): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(117): Previous initialization is here. +fail_compilation/fail9665a.d(132): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(131): Previous initialization is here. +fail_compilation/fail9665a.d(136): Error: immutable field `w` initialized multiple times + Previous initialization is here. +fail_compilation/fail9665a.d(150): Error: static assert: `__traits(compiles, this.v = 1)` is false --- +/ + +/***************************************************/ +// immutable field + struct S1A { immutable int v; @@ -20,14 +46,6 @@ struct S1A } } -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(37): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(42): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(47): Error: immutable field `v` initialized multiple times ---- -+/ struct S1B { immutable int v; @@ -48,14 +66,6 @@ struct S1B } } -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(65): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(70): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(75): Error: immutable field `v` initialized multiple times ---- -+/ struct S1C { immutable int v; @@ -79,16 +89,6 @@ struct S1C /***************************************************/ // with control flow -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(98): Error: immutable field `v` initialization is not allowed in loops or after labels -fail_compilation/fail9665a.d(103): Error: immutable field `v` initialization is not allowed in loops or after labels -fail_compilation/fail9665a.d(108): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(113): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(118): Error: immutable field `v` initialized multiple times ---- -+/ struct S2 { immutable int v; @@ -122,13 +122,6 @@ struct S2 /***************************************************/ // with immutable constructor -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(139): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(143): Error: immutable field `w` initialized multiple times ---- -+/ struct S3 { int v; @@ -147,12 +140,6 @@ struct S3 /***************************************************/ // in __traits(compiles) -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(163): Error: static assert: `__traits(compiles, this.v = 1)` is false ---- -+/ struct S4 { immutable int v;