Issue 20802 - [REG2.088.0] Link failure with writefln
Summary: [REG2.088.0] Link failure with writefln
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: x86_64 Linux
: P1 regression
Assignee: No Owner
URL:
Keywords: link-failure, pull
: 23209 23255 (view as issue list)
Depends on:
Blocks:
 
Reported: 2020-05-06 20:18 UTC by Tim
Modified: 2024-03-04 02:51 UTC (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Tim 2020-05-06 20:18:45 UTC
The following code worked with 2.087.1, but doesn't link since 2.088.0:

//////////////////// test.d ///////////////////
import std.uni, std.stdio;

class C
{
    size_t number;
}

int main()
{
    C inst;

    mixin({
        while(false)
        {
            dchar current;
            CodepointSet(current);
        }
        return "";
    }());
    writefln("%d %d %d", inst.number, C.sizeof, inst.number );
    return 0;
}
///////////////////////////////////////////////

With 2.088.0 it produces the following link error:

/usr/bin/ld: test.o: in function `_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk':
test.d:(.text._D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk[_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk]+0xd6): undefined reference to `_D3std6format__T9getNthIntVAyaa13_696e7465676572207769647468TmTmTmZQBzFNaNfkmmmZi'
/usr/bin/ld: test.d:(.text._D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk[_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk]+0x16f): undefined reference to `_D3std6format__T9getNthIntVAyaa13_696e7465676572207769647468TmTmTmZQBzFNaNfkmmmZi'
/usr/bin/ld: test.d:(.text._D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk[_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk]+0x1c6): undefined reference to `_D3std6format__T9getNthIntVAyaa17_696e746567657220707265636973696f6eTmTmTmZQChFNaNfkmmmZi'
/usr/bin/ld: test.d:(.text._D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk[_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk]+0x254): undefined reference to `_D3std6format__T9getNthIntVAyaa17_696e746567657220707265636973696f6eTmTmTmZQChFNaNfkmmmZi'
/usr/bin/ld: test.d:(.text._D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk[_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk]+0x2a0): undefined reference to `_D3std6format__T9getNthIntVAyaa21_736570617261746f72206469676974207769647468TmTmTmZQCpFNaNfkmmmZi'
/usr/bin/ld: test.d:(.text._D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk[_D3std6format__T14formattedWriteTSQBg5stdio4File17LockingTextWriterTaTmTmTmZQCiFNfKQByMxAammmZk]+0x2c9): undefined reference to `_D3std6format__T6getNthVAyaa19_736570617261746f7220636861726163746572SQCq6traits10isSomeCharTwTmTmTmZQDhFNaNfkmmmZw'
collect2: Fehler: ld gab 1 als Ende-Status zurück

It may be related to issue 17155, but since it was introduced in a different release I have created a separate issue.
Comment 1 Iain Buclaw 2023-01-10 18:17:38 UTC
Now:
---
issue20802.o: in function `_D4core8internal7switch___T14__switch_errorZQrFNaNbNiNfAyamZv':
issue20802.d:(.text._D4core8internal7switch___T14__switch_errorZQrFNaNbNiNfAyamZv[_D4core8internal7switch___T14__switch_errorZQrFNaNbNiNfAyamZv]+0x19): undefined reference to `_D4core9exception__T15__switch_errorTZQsFNaNbNiNeAyamZv'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1
Comment 2 Richard (Rikki) Andrew Cattermole 2024-02-07 11:52:51 UTC
Another more slimmed down version that was experienced by someone on the N.G.

```d
import std.uni, std.stdio;

void main() {
     auto c1 = CodepointSet('a','z'+1);
     writefln("%s", c1);
}
```

```
/dlang/dmd/linux/bin64/../../src/druntime/import/core/internal/switch_.d:189: error: undefined reference to '_D4core9exception__T15__switch_errorTZQsFNaNbNiNeAyamZv'
```

https://forum.dlang.org/post/nvjgpznfizkishhubcuz@forum.dlang.org
Comment 3 Spoov 2024-02-11 11:04:38 UTC
The example can be simplified a bit, same error as in previous comment:

############ test.d ##############
import std.uni;
void main()
{
     CodepointSet('a', 'z');
     dstring s;
     decodeGrapheme(s);
}
##################################

Reducing std.uni yields the code below, same error as above:

#### phobos/std/uni/package.d ####
module std.uni;

enum TransformRes { goOn }

void writeAligned()()
{
    final switch (TransformRes.goOn) { case TransformRes.goOn: break; }
}

struct GcPolicy {}
alias CodepointSet = InversionList!GcPolicy;
struct InversionList(SP=GcPolicy)
{
    this()(uint[] intervals...)
    {
        sanitize();
    }

    void sanitize()
    {
        throw new Exception("");
        writeAligned();
    }
}

void decodeGrapheme(Input)(ref Input inp)
{
    final switch (TransformRes.goOn) { case TransformRes.goOn: break; }
}
##################################

A final switch generates a call to core.internal.switch_.__switch_error for handling the default case.
core.internal.switch_.__switch_error calls core.exception.__switch_errorT.
Both functions are templates.

By default a D program links with libphobos2.a.
libphobos2.a contains sanitize and writeAligned but not __switch_error and __switch_errorT.
This is because libphobos2.a gets build in release mode which causes the call to __switch_error to be replaced with a ud2 instruction.

The linker error shows that __switch_error is present but __switch_errorT is missing.

In the compiler is this code
https://github.com/dlang/dmd/blob/8ab0636400d890a777889b3c84b09bcaa119fd57/compiler/src/dmd/dsymbolsem.d#L4666
which handles the case where a template that got instantiated from a non-root module,
gets instantiated from a root module.
The code fixes the template instantiation, but misses the template instantiations that are caused by it.
Because of this __switch_errorT is not emitted to the object file.

The following three files form a minimal example that reproduces the issue:

############# a.d ################
import b;
extern(C) void main() => bar!true;

############# b.d ################
void missing()() {}
void foo()() => missing();
void bar(bool b)() { version (Release) {} else foo(); }
enum fptr = &bar!false;

########## object.d ##############
// empty to make debugging simpler

$ dmd -c -version=Release b.d
$ dmd a.d b.o
a.o: In function `_D1b__T3fooZQfFNaNbNiNfZv':
a.d:(.text._D1b__T3fooZQfFNaNbNiNfZv[_D1b__T3fooZQfFNaNbNiNfZv]+0x5): undefined reference to `_D1b__T7missingZQjFNaNbNiNfZv'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1
Comment 4 Dlang Bot 2024-02-18 12:21:24 UTC
@tim-dlang created dlang/dmd pull request #16200 "Fix Bugzilla 20802 - Link failure with writefln" fixing this issue:

- Fix Bugzilla 20802 - Link failure with writefln
  
  The issue is caused by compiling druntime/phobos and the application
  with different flags. The template instance for __switch_errorT is not
  included in the build of druntime/phobos, because they are compiled
  with -release. DMD will also not include it in an application compiled
  without -release, because it assumes, that it is already in druntime.
  See comment https://issues.dlang.org/show_bug.cgi?id=20802#c3 by Spoov.
  
  As a workaround the template instance for __switch_errorT is now always
  instantiated in druntime.

https://github.com/dlang/dmd/pull/16200
Comment 5 Dlang Bot 2024-02-18 22:35:06 UTC
dlang/dmd pull request #16200 "Fix Bugzilla 20802 - Link failure with writefln" was merged into stable:

- f4ee5234bccf21a4d2b5ca6d3962ccc3dd7a7d06 by Tim Schendekehl:
  Fix Bugzilla 20802 - Link failure with writefln
  
  The issue is caused by compiling druntime/phobos and the application
  with different flags. The template instance for __switch_errorT is not
  included in the build of druntime/phobos, because they are compiled
  with -release. DMD will also not include it in an application compiled
  without -release, because it assumes, that it is already in druntime.
  See comment https://issues.dlang.org/show_bug.cgi?id=20802#c3 by Spoov.
  
  The template instance for __switch_errorT is now always instantiated in
  druntime, so it is part of the ABI of druntime.

https://github.com/dlang/dmd/pull/16200
Comment 6 anonymous4 2024-02-19 07:28:13 UTC
This looks like a rather fundamental collision between speculative instantiation and conditional compilation. __switch_errorT can't be in druntime unconditionally because its behavior changes depending on compilation options.
Comment 7 Iain Buclaw 2024-02-20 13:22:39 UTC
(In reply to anonymous4 from comment #6)
> This looks like a rather fundamental collision between speculative
> instantiation and conditional compilation. __switch_errorT can't be in
> druntime unconditionally because its behavior changes depending on
> compilation options.
Because it's a weak symbol, any local instantiation would override the unconditional instantiation in druntime.
Comment 8 Iain Buclaw 2024-02-20 13:23:37 UTC
*** Issue 23255 has been marked as a duplicate of this issue. ***
Comment 9 Iain Buclaw 2024-02-20 13:23:59 UTC
*** Issue 23209 has been marked as a duplicate of this issue. ***
Comment 10 Dlang Bot 2024-03-01 23:56:54 UTC
dlang/dmd pull request #16276 "merge stable" was merged into master:

- 6a2fe78ba45b4fcd495f7ab340f784640b9daf01 by Tim Schendekehl:
  Fix Bugzilla 20802 - Link failure with writefln (#16200)
  
  The issue is caused by compiling druntime/phobos and the application
  with different flags. The template instance for __switch_errorT is not
  included in the build of druntime/phobos, because they are compiled
  with -release. DMD will also not include it in an application compiled
  without -release, because it assumes, that it is already in druntime.
  See comment https://issues.dlang.org/show_bug.cgi?id=20802#c3 by Spoov.
  
  The template instance for __switch_errorT is now always instantiated in
  druntime, so it is part of the ABI of druntime.

https://github.com/dlang/dmd/pull/16276