GHC makes unsound references in object code
To reproduce the bug run script run.sh
from the attached archive.
It will:
- install
FooPackage
Resolving dependencies...
Configuring FooPackage-0.1...
Building FooPackage-0.1...
Preprocessing library FooPackage-0.1...
[1 of 1] Compiling FooPackage ( src\FooPackage.hs, dist\build\FooPackage.o )
In-place registering FooPackage-0.1...
Installing library in
C:\Users\Anton\AppData\Roaming\cabal\i386-windows-ghc-7.6.3\FooPackage-0.1
Registering FooPackage-0.1...
Installed FooPackage-0.1
}}}
2. compile executable `Client1` which depends on `FooPackage`
{{{
[1 of 3] Compiling QuxClient ( QuxClient.hs, obj\QuxClient.o )
[2 of 3] Compiling BarClient ( BarClient.hs, obj\BarClient.o )
[3 of 3] Compiling Client1 ( Client1.hs, obj\Client1.o )
Linking exes/Client1.exe ...
}}}
3. compile executable `Client2` which doesn't depend on `FooPackage`
At the third step GHC will fall with linker error:
{{{
[2 of 2] Compiling Client2 ( Client2.hs, obj\Client2.o )
Linking exes/Client2.exe ...
obj\BarClient.o:fake:(.text+0x83): undefined reference to `FooPackagezm0zi1_FooPackage_zdsinsertzuzdsgo5_info'
obj\BarClient.o:fake:(.data+0x10): undefined reference to `FooPackagezm0zi1_FooPackage_zdsinsertzuzdsgo5_closure'
collect2: ld returned 1 exit status
}}}
Both `Client1` and `Client2` import `BarClient` module that doesn't depends on `FooPackage`.
`Client1` imports `QuxClient` that imports `FooPackage`:
{{{
module QuxClient where
import FooPackage(foo)
import Data.Set
qux :: Set String -> Set String
qux = foo
module BarClient where
import Data.Set
bar :: Set String -> Set String
bar s = insert "bar" s
FooPackage
uses function Data.Set.insert
which is marked at Data.Set
as INLINABLE
:
module FooPackage where
import Data.Set
foo :: Set String -> Set String
foo s = insert "foo" s
GHC emphasizes in interface file, that FooPackage.o
contains specialized version of Data.Set.insert
:
> ghc --show-iface FooPackage.hi
...
"SPEC Data.Set.Base.insert [GHC.Base.String]" [ALWAYS] forall $dOrd :: GHC.Classes.Ord
GHC.Base.String
Data.Set.Base.insert @ [GHC.Types.Char] $dOrd = FooPackage.$sinsert
...
Later GHC see again the use of Data.Set.insert@String
at BarClient
and decides to apply this specialise rule, so that BarClient
now has reference to FooPackage
:
> ghc --show-iface BarClient.hi
...
bar :: Data.Set.Base.Set GHC.Base.String
-> Data.Set.Base.Set GHC.Base.String
{- Arity: 1, Strictness: S,
Unfolding: (\ s :: Data.Set.Base.Set GHC.Base.String ->
FooPackage.$sinsert_$sgo5 BarClient.bar1 s) -}
...
Client2
doesn't depend on FooPackage
, thus linker throws an error.
There are plenty of workarounds here, but all of them are ad hoc (1,2,3) or could affect performance (4):
- Change the order of
BarClient
andQuxClient
imports inClient1
module. - Compile
Client1
andClient2
in opposite order. - Add fake import at
Client2
module:
import FooPackage()
- Build
FooPackage
with ghc-option-fomit-interface-pragmas
. That will eraise fromFooPackage.hi
all specialisation rules as well as information about strictness and inlining.
Some information about the environment:
> ghc-pkg list
...
containers-0.5.0.0
base-4.6.0.1
...
> ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.6.3
> cabal --version
cabal-install version 1.18.0.3
using version 1.18.1.3 of the Cabal library
Trac metadata
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |