I unfortunately only have a complex testcase for this issue (after long dustmiting sessions and manual reduction), involving three files (one.d, two.d, three.d).
When only `one.d` is compiled, function `three.TT!(int).insertabcdefg(int)` is deduced to be `@nogc`. But the deduction happens late, and printing `.mangleof` does not show it. The symbol in the object file does show `@nogc` in the mangling.
When `one.d` and `two.d` are compiled simultaneously (with or without `three.d`), the function is no longer deduced to be `@nogc`.
(The bug was found in Weka's codebase where everything-at-once compilation is impossible, and where per-package compilation is done. The testcase is heavily simplified.)
Details:
Only compiling one.d gives:
> dmd one.d -c -of=tmp1.o
_D5three9__T2TTTiZ2TT13insertabcdefgMFiZv
> nm tmp1.o | grep "insertabcdefg"
0000000000000358 S _D5three9__T2TTTiZ2TT13insertabcdefgMFNiiZv
Compiling all three at once gives:
> dmd one.d two.d three.d -c -of=tmp2.o
_D5three9__T2TTTiZ2TT13insertabcdefgMFiZv
> nm tmp2.o | grep "insertabcdefg"
0000000000000460 S _D5three9__T2TTTiZ2TT13insertabcdefgMFiZv
File one.d
```````````````````````````
module one;
import two;
import three;
struct BB
{
enum MAX_NUM_FIBERS = 4096;
TWOR!1 t;
TT!(int) tt;
auto foo()
{
tt.insertabcdefg(1);
}
}
BB bb;
```````````````````````````
File two.d
```````````````````````````
module two;
import one;
struct ET(bool a)
{
enum e = BB.MAX_NUM_FIBERS;
}
alias Event = ET!false;
struct TWOR(size_t M)
{
Event e;
void open()
{
bb.foo();
}
}
```````````````````````````
File three.d
```````````````````````````
module three;
void aaa() @nogc
{
}
struct TT(T)
{
void insertabcdefg(T) // @nogc <-- deduction problem
{
pragma(msg, insertabcdefg.mangleof);
aaa();
}
}
```````````````````````````
Comment #1 by johanengelen — 2017-06-23T10:57:44Z
(the mangling difference between compiling `one.d` with or without `two.d` leads to linking problems at Weka)
Comment #2 by johanengelen — 2017-06-23T12:43:55Z
In other code I hit the same issue but now with compiler-invocation-dependent deduction of `@safe` or `@trusted` for `__fieldDtor` of a struct.
Comment #3 by johanengelen — 2017-06-23T13:33:47Z
And in other cases, `pure` is deduced depending on compiler invocation, and is actually deduced wrong (function is not pure but is deduced `pure`).
Comment #4 by johanengelen — 2017-07-14T16:47:47Z
This problem is bigger than just templates.
I am seeing more and more deduction errors, resulting in linker errors.
reopening, because it is not fixed for safe/trusted, nor for impure/pure, throw/nothrow.
Comment #10 by dlang-bot — 2020-03-23T09:08:05Z
@WalterBright created dlang/dmd pull request #10959 "add pure and @safe to correct for issue 17541" mentioning this issue:
- add pure and @safe to correct for issue 17541
https://github.com/dlang/dmd/pull/10959
Comment #11 by bugzilla — 2020-03-24T08:27:49Z
I cannot reproduce the problem. The previous fix I pushed was wrong.
The pragma(msg, insertabcdefg.mangleof) gives the wrong name mangling, as it happens before the inference is complete. The compiler should give an error for that.
Comment #12 by johanengelen — 2020-03-24T18:57:40Z
(In reply to Walter Bright from comment #11)
> I cannot reproduce the problem. The previous fix I pushed was wrong.
>
> The pragma(msg, insertabcdefg.mangleof) gives the wrong name mangling, as it
> happens before the inference is complete. The compiler should give an error
> for that.
The pragma(msg, insertabcdefg.mangleof) is not the only bug here. The larger pain point is that deduction depends on compiler invocation and hence breaks separate compilation (either for linking symbol resolution, or for code depending on a certain attribute deduction result).
Tested with dlang 2.090.1 just now, replacing `@nogc` with `pure`, and following reproduction commands shows the problem:
❯ ~/dlang/dmd20901/osx/bin/dmd one.d -c -of=tmp1.o
❯ nm tmp1.o| grep "insertabc"
00000000000002ac S __D5three__T2TTTiZQg13insertabcdefgMFNaiZv
0000000000000078 S __D5three__T2TTTiZQg13insertabcdefgMFNaiZv.eh
❯ dmd one.d two.d three.d -c -of=tmp2.o
❯ nm tmp2.o | grep "insertabc"
00000000000003b4 S __D5three__T2TTTiZQg13insertabcdefgMFiZv
00000000000000c0 S __D5three__T2TTTiZQg13insertabcdefgMFiZv.eh
The symbol names in the binary are different.
Comment #13 by snarwin+bugzilla — 2023-02-17T21:14:00Z
Another example, from Discord:
--- test.d
module test;
void main(){
import test2;
MyStruct.RcPtr gl_indcies = MyStruct.RcPtr(MyStruct());
}
--- test2.d
module test2;
struct MyStruct{
alias Ptr = Unique!MyStruct;
alias RcPtr = SafeRefCounted!MyStruct;
~this(){}
}
struct SafeRefCounted(T){
struct RefCountedStore{
struct Impl{
T _payload;
}
Impl* _store;
}
RefCountedStore* _refCounted;
this(T)(T arg){}
~this(){
destroy(_refCounted._store._payload);
}
}
struct Unique(T){
alias RefT = T*;
void opAssign(U)(Unique!U u) if (is(u.RefT:RefT)){}
~this(){
destroy(*_p);
}
RefT _p;
}
---
When compiled together, there is no error:
---
$ dmd -c test.d test2.d && dmd test.o test2.o
$ echo $?
0
---
When compiled separately, linking fails:
---
$ dmd -c test.d && dmd -c test2.d && dmd test.o test2.o
/usr/bin/ld: test.o: in function `_Dmain':
test.d:(.text._Dmain[_Dmain]+0x2f): undefined reference to `_D5test2__T14SafeRefCountedTSQBb8MyStructZQBf6__dtorMFZv'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1
---
Examining the object files shows that this is due to mismatched attributes:
---
$ nm test.o | ddemangle | grep 'SafeRefCounted.*__dtor'
U void test2.SafeRefCounted!(test2.MyStruct).SafeRefCounted.__dtor()
$ nm test2.o | ddemangle | grep 'SafeRefCounted.*__dtor'
0000000000000000 W pure nothrow @nogc @safe void test2.SafeRefCounted!(test2.MyStruct).SafeRefCounted.__dtor()
---
Reproduced with DMD 2.102.0 on 64-bit Linux.
Comment #14 by snarwin+bugzilla — 2023-02-26T00:19:18Z
*** Issue 23723 has been marked as a duplicate of this issue. ***
Comment #15 by dlang-bot — 2023-08-12T18:54:32Z
@ljmf00 created dlang/dmd pull request #15534 "Fix Issue 17541 - @safe/pure/nothrow attribute deduction depends on c…" fixing this issue:
- Fix Issue 17541 - @safe/pure/nothrow attribute deduction depends on compile invocation
A reboot of #10959 follow-up plus implementation for `nothrow`.
Signed-off-by: Luís Ferreira <[email protected]>
https://github.com/dlang/dmd/pull/15534
Comment #16 by robert.schadek — 2024-12-13T18:52:42Z