Bug 18868 – Separate compilation generates two static this functions, runs it twice

Status
RESOLVED
Resolution
FIXED
Severity
critical
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-05-16T21:34:27Z
Last change time
2018-07-04T21:45:06Z
Keywords
industry
Assigned to
No Owner
Creator
johanengelen

Comments

Comment #0 by johanengelen — 2018-05-16T21:34:27Z
Testcase that shows that the static this is called twice. ``` // File: fls.d template FLS(T) { int offset = -1; static this() { assert (offset == -1); offset += 2; } } ``` ``` // File: a.d shared static this() { } import fls; alias floop = FLS!(int); ``` ``` // File: b.d import fls; alias floop = FLS!(int); void main() {} ``` Build+run script: ``` ~/weka/ldc/installcurrent/bin/ldc2 -c a.d -of=a.o ~/weka/ldc/installcurrent/bin/ldc2 -c b.d -of=b.o ~/weka/ldc/installcurrent/bin/ldc2 fls.d a.o b.o ./fls ``` Fails with DMD (tested with 2.080) and LDC. What is happening: 1. Because of separate compilation and template instantiation, the static ctor is codegenned in two compilation units and added to both moduleinfos such that it will be called on startup of each module (`a` and `b`) 2. After linking, two modules will call that same static ctor. 3. The static ctor contains code to catch multiple call case! Upon second or more calls it simply returns. The checking is using a __gate variable. For example: `....._sharedStaticCtor67().__gate` However, as you can see there is this number "67" there. It is a number that is deterministic but may vary across separate compilations depending, among others, on how many other static ctors there are. 4. In one compilation case, the number is "x", in the other compilation case the number is "not x". Thus we now have two _different_ staticctor functions and gate variables, that access and write to the _same_ `offset` variable. So the appearance is that the static ctor is run twice. This is a similar problem as what we had with unittest function naming that was semi-non-deterministic. https://issues.dlang.org/show_bug.cgi?id=16995 https://issues.dlang.org/show_bug.cgi?id=18097 https://github.com/dlang/dmd/pull/7761 I think the fix is easy: instead of using a counter number, use the line+column number like what we do now for the unittest functions.
Comment #1 by johanengelen — 2018-05-16T22:50:42Z
Comment #2 by johanengelen — 2018-05-19T22:23:34Z
Extra testcase that must be added and considered (currently works): ``` static foreach(s; ["666", "777"]) { mixin(genCtor(s)); } int i; string genCtor(string a) { return "static this() { i += " ~ a ~ "; }"; } void main() { assert(i == 0 + 666 + 777); } ```
Comment #3 by github-bugzilla — 2018-05-28T18:18:20Z
Commits pushed to master at https://github.com/dlang/dmd https://github.com/dlang/dmd/commit/849155631cba3017566d3160cf15cb40584abf10 fix Issue 18868 - nondeterministic static ctor/dtor Instead of using a counter to create unique static ctor and dtor function identifiers, use the deterministic line+column location that is also used for unittests. The further disambiguate mixin instantiations on the exact same location, use a local counter. https://github.com/dlang/dmd/commit/cf19b78d47572e1c6d1a79687ea592b051552e93 Merge pull request #8255 from JohanEngelen/fix18868 fix Issue 18868 - nondeterministic static ctor/dtor merged-on-behalf-of: Jacob Carlborg <[email protected]>
Comment #4 by default_357-line — 2018-06-04T13:21:29Z
This also fixes this issue: static foreach (entry; ["foo", "bar", "baz"]) { unittest { writeln(entry); } } foo foo foo which happened because all three unittest blocks had the same identifier, based on line number.
Comment #5 by atila.neves — 2018-07-04T21:42:21Z
The fix for this issue introduces a regression and breaks usage of __traits(getUnitTests) and separate compilation. Issue here: https://issues.dlang.org/show_bug.cgi?id=19058
Comment #6 by atila.neves — 2018-07-04T21:45:06Z
I forgot to mention that the issue here is the same issue as before with the unittest names - counters can't and won't work, because the number will vary depending on how the compiler is invoked.