GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2019-07-07T18:17:14Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/14382The 'impossible' happened whilst installing gi-gtk via cabal2019-07-07T18:17:14ZmaartenjacobsThe 'impossible' happened whilst installing gi-gtk via cabalI've been trying to install gi-gtk using "cabal install gi-gtk". In the process I upgraded to the most recent stable GHC and cabal. I've also installed gtk3 libraries which I thought were necessary.
```
...
[91 of 95] Compiling GI.Pango...I've been trying to install gi-gtk using "cabal install gi-gtk". In the process I upgraded to the most recent stable GHC and cabal. I've also installed gtk3 libraries which I thought were necessary.
```
...
[91 of 95] Compiling GI.Pango.Objects.Layout ( GI/Pango/Objects/Layout.hs, dist/build/GI/Pango/Objects/Layout.o )
ghc: panic! (the 'impossible' happened)
(GHC version 8.2.1 for x86_64-unknown-linux):
tcIfaceGlobal (local): not found
You are in a maze of twisty little passages, all alike.
While forcing the thunk for TyThing Layout
which was lazily initialized by initIfaceCheck typecheckLoop,
I tried to tie the knot, but I couldn't find Layout
in the current type environment.
If you are developing GHC, please read Note [Tying the knot]
and Note [Type-checking inside the knot].
Consider rebuilding GHC with profiling for a better stack trace.
Contents of current type environment: []
Call stack:
CallStack (from HasCallStack):
prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1133:58 in ghc:Outputable
callStackDoc, called at compiler/utils/Outputable.hs:1137:37 in ghc:Outputable
pprPanic, called at compiler/iface/TcIface.hs:1696:23 in ghc:TcIface
Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug
Failed to install gi-pango-1.0.15
cabal: Error: some packages failed to install:
gi-gdk-3.0.14-INozoUgbf2HFkX3VeIwKfl depends on gi-gdk-3.0.14 which failed to
install.
gi-gtk-3.0.17-1AObKz0Ppj5GXVupmEC7Yc depends on gi-gtk-3.0.17 which failed to
install.
gi-pango-1.0.15-E4HLwHGC2n62ObUIQeuwp8 failed during the building phase. The
exception was:
ExitFailure 1
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.2.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"The 'impossible' happened whilst installing gi-gtk via cabal","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.2.1","keywords":["gtk,","pango"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"I've been trying to install gi-gtk using \"cabal install gi-gtk\". In the process I upgraded to the most recent stable GHC and cabal. I've also installed gtk3 libraries which I thought were necessary. \r\n\r\n{{{\r\n...\r\n[91 of 95] Compiling GI.Pango.Objects.Layout ( GI/Pango/Objects/Layout.hs, dist/build/GI/Pango/Objects/Layout.o )\r\nghc: panic! (the 'impossible' happened)\r\n (GHC version 8.2.1 for x86_64-unknown-linux):\r\n\ttcIfaceGlobal (local): not found\r\n You are in a maze of twisty little passages, all alike.\r\n While forcing the thunk for TyThing Layout\r\n which was lazily initialized by initIfaceCheck typecheckLoop,\r\n I tried to tie the knot, but I couldn't find Layout\r\n in the current type environment.\r\n If you are developing GHC, please read Note [Tying the knot]\r\n and Note [Type-checking inside the knot].\r\n Consider rebuilding GHC with profiling for a better stack trace.\r\n Contents of current type environment: []\r\n Call stack:\r\n CallStack (from HasCallStack):\r\n prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1133:58 in ghc:Outputable\r\n callStackDoc, called at compiler/utils/Outputable.hs:1137:37 in ghc:Outputable\r\n pprPanic, called at compiler/iface/TcIface.hs:1696:23 in ghc:TcIface\r\n\r\nPlease report this as a GHC bug: http://www.haskell.org/ghc/reportabug\r\n\r\nFailed to install gi-pango-1.0.15\r\ncabal: Error: some packages failed to install:\r\ngi-gdk-3.0.14-INozoUgbf2HFkX3VeIwKfl depends on gi-gdk-3.0.14 which failed to\r\ninstall.\r\ngi-gtk-3.0.17-1AObKz0Ppj5GXVupmEC7Yc depends on gi-gtk-3.0.17 which failed to\r\ninstall.\r\ngi-pango-1.0.15-E4HLwHGC2n62ObUIQeuwp8 failed during the building phase. The\r\nexception was:\r\nExitFailure 1\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->8.2.3https://gitlab.haskell.org/ghc/ghc/-/issues/24436ghc -M fails to correctly account for indirect SOURCE imports2024-03-11T15:23:12ZBen Gamarighc -M fails to correctly account for indirect SOURCE importsConsider a program such as:
```haskell
--- in the unit1 unit
-- Foo.hs-boot
module Foo where
foo :: String
-- Foo.hs
module Foo where
foo :: String
foo = "hi"
-- ... additional cyclic bindings here which make `Foo.hs-boot` necessary
-...Consider a program such as:
```haskell
--- in the unit1 unit
-- Foo.hs-boot
module Foo where
foo :: String
-- Foo.hs
module Foo where
foo :: String
foo = "hi"
-- ... additional cyclic bindings here which make `Foo.hs-boot` necessary
-- Bar.hs
module Bar where
import {-# SOURCE #-} Foo
bar = ... foo ...
--- in the unit2 unit
-- Baz.hs
module Baz where
import Bar
baz = ... bar ...
```
Imagine running `ghc -M` on `Baz.hs`. It will contain something like the following:
```makefile
Baz.o : Bar.hs Bar.o
Bar.o : Bar.hs Foo.o-boot
Foo.o : Foo.hs
Foo.o-boot : Foo.hs-boot
```
This seems perfectly reasonable: it captures the dependency structure of the user's program.
However, if we consider what happens during one-shot compilation, we will see that it isn't quite right. Specifically, imagine that `baz` get an unfolding, which will naturally contain a reference to `Bar.bar` (which was `SOURCE` imported). When we compile `Baz` GHC will see that:
1. we need a declaration for `Foo.foo` in order to unfold `Bar.bar`
2. the user hasn't imported `Bar` directly; this means that `GHC.Iface.Load.importDecl` will call `loadInterface` with `ImportBySystem`
3. `GHC.Iface.Load.loadInterface` calls `GHC.Iface.Load.wantHiBootFile` to determine what it should load
4. `wantHiBootFile` sees that `Foo` is not in the unit currently being compiled. Consequently, it responds with `NotBoot`
5. GHC attempts to load `Foo.hi` instead of `Foo.hi-boot` as the `ghc -M` output claimshttps://gitlab.haskell.org/ghc/ghc/-/issues/23531Panic with bad representation polymorphism + hs-boot2023-06-20T15:04:24ZKrzysztof GogolewskiPanic with bad representation polymorphism + hs-bootGiven
X.hs-boot
```haskell
{-# LANGUAGE TypeFamilies #-}
module X where
import GHC.Exts
type family T :: RuntimeRep
f :: forall (a :: TYPE T). a
```
X.hs
```haskell
module X where
import Y
```
Y.hs
```haskell
module Y where
import ...Given
X.hs-boot
```haskell
{-# LANGUAGE TypeFamilies #-}
module X where
import GHC.Exts
type family T :: RuntimeRep
f :: forall (a :: TYPE T). a
```
X.hs
```haskell
module X where
import Y
```
Y.hs
```haskell
module Y where
import {-# SOURCE #-} X
g () = f
```
compiling `ghc X` crashes:
```
[2 of 3] Compiling Y ( Y.hs, Y.o )
<no location info>: error:
panic! (the 'impossible' happened)
GHC version 9.7.20230615:
isUnliftedType
forall (a :: TYPE T). a :: TYPE T
Call stack:
CallStack (from HasCallStack):
callStackDoc, called at compiler/GHC/Utils/Panic.hs:191:37 in ghc-9.7-inplace:GHC.Utils.Panic
pprPanic, called at compiler/GHC/Core/Type.hs:2295:22 in ghc-9.7-inplace:GHC.Core.Type
isUnliftedType, called at compiler/GHC/StgToCmm/Closure.hs:211:5 in ghc-9.7-inplace:GHC.StgToCmm.Closure
```
I suspect this will be fixed by #22109, when we check that top-level binds are lifted in the typechecker rather than the desugarer.https://gitlab.haskell.org/ghc/ghc/-/issues/23527Panic with a bad hs-boot file2023-06-15T14:53:31ZKrzysztof GogolewskiPanic with a bad hs-boot fileGiven two files:
M.hs-boot
```haskell
{-# LANGUAGE TypeFamilies #-}
module M where
import Data.Kind
type family T :: Type
f :: forall (x :: T). Maybe x
```
M.hs
```haskell
module M where
```
attempting to compile `ghc M.hs` crashes in...Given two files:
M.hs-boot
```haskell
{-# LANGUAGE TypeFamilies #-}
module M where
import Data.Kind
type family T :: Type
f :: forall (x :: T). Maybe x
```
M.hs
```haskell
module M where
```
attempting to compile `ghc M.hs` crashes in 9.4 and above:
```
[1 of 2] Compiling M[boot] ( M.hs-boot, M.o-boot )
<no location info>: error:
panic! (the 'impossible' happened)
GHC version 9.7.20230606:
Can't serialise IfaceHoleCo
```https://gitlab.haskell.org/ghc/ghc/-/issues/21094Redudant hs-boot file checks are not very good2022-08-15T14:37:52ZMatthew PickeringRedudant hs-boot file checks are not very goodFor example see test `T20030_test3` has a redundant boot file N.hs-boot but there is no warning for it.
There a few tests with redundant hs-boot files which are not caught by warnings about redundant boot files:
* T14075
* T20030_test3...For example see test `T20030_test3` has a redundant boot file N.hs-boot but there is no warning for it.
There a few tests with redundant hs-boot files which are not caught by warnings about redundant boot files:
* T14075
* T20030_test3
* recomp-boot2
* recomp-boot-dyn-too
* recomp-boot
We could make this a bit better to catch cases where the module doesn't depend on the boot file itself and therefore warn it's redundant.Matthew PickeringMatthew Pickeringhttps://gitlab.haskell.org/ghc/ghc/-/issues/20587Weird interaction between source imports and superclasses2022-02-09T11:22:21ZDavid FeuerWeird interaction between source imports and superclasses## Summary
Under as-yet-undetermined circumstances, GHC fails to recognize superclass relationships when `{-# SOURCE #-}` imports are used.
## Steps to reproduce
I have not yet been able to produce a small example triggering this prob...## Summary
Under as-yet-undetermined circumstances, GHC fails to recognize superclass relationships when `{-# SOURCE #-}` imports are used.
## Steps to reproduce
I have not yet been able to produce a small example triggering this problem, though I have tried. As of GHC 9.2, `GHC.Base` `{-# SOURCE #-}` imports `GHC.Real` (which defines the `Integral` class) and `Data.Semigroup.Internal` to break a cycle. In !6850, I try to replace the definition of `stimes` for `Endo a` (in `Data.Semigroup.Internal`) with a direct one, rather than defining `stimes = stimesMonoid` (where `stimesMonoid` is also defined in `Data.Semigroup.Internal`). Unfortunately, GHC fails to see, in the `stimes` definition, that `Num` and `Ord` are superclasses of `Integral`. This is very strange, because it has no trouble seeing that when compiling the various functions in `Data.Semigroup.Internal`!
## Expected behavior
I expect everything to compile.
## Environment
* GHC version used: recent master: 638f65482ca5265c268aa97abfcc14cdc27e46ba
Optional:
* Operating System:
* System Architecture:https://gitlab.haskell.org/ghc/ghc/-/issues/20447Assert that we don't try to look up an Id too early (Occurrence is GlobalId, ...2021-10-04T13:07:19Zsheafsam.derbyshire@gmail.comAssert that we don't try to look up an Id too early (Occurrence is GlobalId, but binding is LocalId)It is not always OK to look up the `IdInfo` of an `Id` in the presence of `hs-boot` files. An example test case is `T10083`: if one tries to inspect `IdInfo`s early on in the typechecker (e.g. in the `GHC.Tc.Gen` namespace), one can get ...It is not always OK to look up the `IdInfo` of an `Id` in the presence of `hs-boot` files. An example test case is `T10083`: if one tries to inspect `IdInfo`s early on in the typechecker (e.g. in the `GHC.Tc.Gen` namespace), one can get a `Occurrence is GlobalId, but binding is LocalId` Core Lint error.
@simonpj diagnoses the problem in https://gitlab.haskell.org/ghc/ghc/-/issues/20200#note_378625:
> We have:
>
> * `T10083.hs-boot`, which defines `eqRSR`
> * `T10083a.hs`, which defines `eqSR`, whose RHS mentions `eqRSR`
> * `T10083.hs`, which defines `eqRSR`, whose RHS mentions `eqSR`.
>
> Now when compiling T10083.hs,
>
> * we typecheck the defn of `eqRSR`
> * so we need the Id (and hence Type) for `eqSR`
> * but `eqSR`'s unfolding mentions `eqRSR`, whose Id doesn't yet exist, because we havn't typechecked it.
>
> If we aggressively suck in that unfolding for `eqSR`, we'll be forced to find an Id for `eqRSR`; since it hasn't yet been built, we'll get it from the Iface from the hs-boot file ... a GlobalId. And that is the problem.
>
> Supppose we *have* finished typechecking and *now* we force that unfolding. How come we now get the typechecked defn of the Id? Answer: see GHC.IfaceToCore.tcIfaceGlobal:
>
> * It looks in `if_rec_types env` to get an IO action `get_type_env`
> * That in turn reads a ref-cell which contains the bindings for locally-defined Ids. (These will be LocalIds.
> * Somehow we update the ref-cell after typechecking. I can't see exactly where that happens. There is a maze of twisty little passages concerning `KnotVars`, and my brain exploded.
> * But if we read that ref-cell too early it'll be empty.
> * And so `tcIfaceGlobal` will look in the Iface for the hi-boot file instead.
>
> Or something like that.
Simon recommends adding an assertion to ensure we don't accidentally look up `IdInfo`s too early:
> I think that in `tcIfaceGlobal` if `cur_mod` = `nameModule name` then we should jolly well get a hit in `if_rec_types`; if not, fail with an error saying you have read `if_rec_types` too early. Or... hmm.... maybe A.hs-boot has exported something that A.hs doesn't actually define... that might happen too.
This ticket tracks this task.sheafsam.derbyshire@gmail.comsheafsam.derbyshire@gmail.comhttps://gitlab.haskell.org/ghc/ghc/-/issues/20366o-boot files still are written with -fno-code2021-09-15T15:53:26ZMatthew Pickeringo-boot files still are written with -fno-codeIf you have a hs-boot file then an empty `o-boot` file is still created when you use `-fno-code` even though no object files are created.If you have a hs-boot file then an empty `o-boot` file is still created when you use `-fno-code` even though no object files are created.Matthew PickeringMatthew Pickeringhttps://gitlab.haskell.org/ghc/ghc/-/issues/19855Wired-in names in hs-boot files are not fully checked2021-05-20T11:45:02ZSylvain HenryWired-in names in hs-boot files are not fully checked#19638 was due to a declaration in a hs-boot file of ghc-bignum not matching the declaration in the corresponding hs file.
In https://gitlab.haskell.org/ghc/ghc/-/issues/19638#note_352989 @mpickering noticed that it is because wired-in ...#19638 was due to a declaration in a hs-boot file of ghc-bignum not matching the declaration in the corresponding hs file.
In https://gitlab.haskell.org/ghc/ghc/-/issues/19638#note_352989 @mpickering noticed that it is because wired-in names bypass some checks (see https://gitlab.haskell.org/ghc/ghc/-/blob/fc9546caf3e16db070bfc7bb5523c38595233e26/compiler/GHC/Tc/Module.hs#L870)
We should be more selective about the wired-in names that need this special treatment (which has been added in 5ad61e1470db6dbc8279569c5ad1cc093f753ac0).
Moreover the comment there says that it's used in particular for `GHC.Err.error` because it's a rather gross hack, but `libraries/base/GHC/Err.lhs-boot` doesn't exist since 404327a9d9ce38804c5e407eaf2a4167e9a3c906 and `error` isn't wired-in since a7b751db766bd456ace4f76a861e5e8b927d8f17.https://gitlab.haskell.org/ghc/ghc/-/issues/19816SOURCE imported things shouldn't be allowed to be used in TH splices2021-05-11T03:27:18ZMatthew PickeringSOURCE imported things shouldn't be allowed to be used in TH splicesIn a module loop, if you use something which is source imported in a TH splice then you get an error during linking the expression.
It seems this is a bit late during compilation and the error should be raised earlier, because there's ...In a module loop, if you use something which is source imported in a TH splice then you get an error during linking the expression.
It seems this is a bit late during compilation and the error should be raised earlier, because there's never going to be an object file for the .hs-boot file.
https://gist.github.com/522acd04b479217e0e728ada01f309f3
```
[1 of 3] Compiling C[boot] ( C.hs-boot, C.o-boot, C.dyn_o )
[2 of 3] Compiling D ( D.hs, D.o, D.dyn_o )
[3 of 3] Compiling C ( C.hs, C.o, C.dyn_o )
module C cannot be linked; it is only available as a boot module
```https://gitlab.haskell.org/ghc/ghc/-/issues/19352scope-check hs-boot files correctly2021-02-21T16:08:33Zdailecticscope-check hs-boot files correctly## Summary
For the purpose of ambiguity checks, GHC compares all the identifiers in the total module even if they don't appear in the `{-# source #-}` imported hs-boot.
## Steps to reproduce
A.hs-boot: `module A where`
A.hs:
```
mod...## Summary
For the purpose of ambiguity checks, GHC compares all the identifiers in the total module even if they don't appear in the `{-# source #-}` imported hs-boot.
## Steps to reproduce
A.hs-boot: `module A where`
A.hs:
```
module A where
pattern X = ()
```
B.hs-boot:
```
module B where
```
B.hs:
```
module B where
pattern X = ()
```
C.hs:
```
module C (module X) where
import {-# source #-} A as X
import {#- source #-} B as X
```
test.cabal
```
cabal-version: 3.4
name: test
version: 0.0.1.0
library
exposed-modules: A,B,C
build-depends: base == 4.15.0.0
default-extensions: PatternSynonyms
```
`cabal build`
## Expected behavior
GHC should not complain about ambiguous occurrence of `A` in C.hs, because they aren't actually in scope and aren't exported.
Instead, it says
```
C.hs:1:11: error:
Conflicting exports for ‘X’:
‘module X’ exports ‘X.X’ imported from ‘A’ at C.hs:2:1-28
‘module X’ exports ‘X.X’ imported from ‘B’ at C.hs:3:1-28
```
## Environment
* GHC version used: 9.0.1
* Cabal version 3.4.0.0
Optional:
* Operating System: Ubuntu 20.10
* System Architecture: x86_64https://gitlab.haskell.org/ghc/ghc/-/issues/19151"unknown symbol" arising from hs-boot file2021-05-09T21:07:43Zsheafsam.derbyshire@gmail.com"unknown symbol" arising from hs-boot file## Summary
When building my [fir](https://gitlab.com/sheaf/fir) library, I sometimes run into issues with `unknown symbol` which seem to arise from `hs-boot` files.
## Description
Here's one instance of the problem. The library includ...## Summary
When building my [fir](https://gitlab.com/sheaf/fir) library, I sometimes run into issues with `unknown symbol` which seem to arise from `hs-boot` files.
## Description
Here's one instance of the problem. The library includes the following:
`FIR.ProgramState.hs-boot`
```haskell
module FIR.ProgramState where
data ProgramState
```
`FIR.AST.Type`
```haskell
module FIR.AST.Type where
[...]
import {-# SOURCE #-} FIR.ProgramState
( ProgramState )
[...]
```
This seems to occasionally cause problems of the following sort:
```
ghc.exe: | dist-newstyle\build\x86_64-windows\ghc-8.10.3\fir-0.0.1.0\build\FIR\AST\Type.o: unknown symbol `firzm0zi0zi1zi0zminplace_FIRziProgramState_zdtcProgramState_closure'
ghc.exe: Could not load Object Code dist-newstyle\build\x86_64-windows\ghc-8.10.3\fir-0.0.1.0\build\FIR\AST\Type.o.
```
Here GHC is complaining that the module `FIR.AST.Type` can't find `FIR.ProgramState_$tcProgramState_closure`, which I assume refers to the type constructor `ProgramState`.
## Steps to reproduce
Unfortunately I don't have a reliable reproducer, but compiling the library from scratch
```
git clone https://gitlab.com/sheaf/fir
cd fir
cabal build fir
````
seems to run into the issue more frequently than when re-building.
I think this might be caused by the fact that the components in the module dependency graph containing these `hs-boot` files end up being rather large, of the order of ~ 40 modules (I don't know how to measure it precisely), which somehow trips up the Windows linker code? But this is a complete stab in the dark on my part.
Does anyone have advice about how to go about reproducing this issue? I'm thinking I could write code to generate some large modules, with recursive imports that create large components, to deliberately cause this issue to occur. Any ideas?
## Environment
The above error arose from GHC 8.10.3, although I've been seeing this error on GHC 8.8 and previous 8.10 release too. This is on Windows 10 x64.https://gitlab.haskell.org/ghc/ghc/-/issues/17490Misleading error in boot file when superclass constraint is present but metho...2019-12-10T21:33:25ZAndreas AbelMisleading error in boot file when superclass constraint is present but methods are absent## Summary
In boot files, when superclass constraint is present but class methods are omitted, the error message talks about incompatible method count and MINIMAL pragmas.
I think when the keyword `where` and the methods are omitted in...## Summary
In boot files, when superclass constraint is present but class methods are omitted, the error message talks about incompatible method count and MINIMAL pragmas.
I think when the keyword `where` and the methods are omitted in the `class` declaration in the boot file, GHC should assume that the user chose not to repeat the methods in the boot file, rather than complaining.
If it is mandatory to omit the superclass constraints always when the methods are omitted, GHC should give the user a hint in the error message, like "Class methods can only be omitted when superclass constraints are omitted as well".
## Steps to reproduce
```haskell
module A where
import B
class Eq a => Diag a where
diag :: a -> Bool
instance Diag Int where
diag = bar
```
```haskell
module B where
import {-# SOURCE #-} A
bar :: Diag a => a -> Bool
bar = undefined
```
Boot file ``A.hs-boot``:
```haskell
module A where
-- class Diag a -- WORKS
class Eq a => Diag a
-- A.hs-boot:5:1: error:
-- Class ‘Diag’ has conflicting definitions in the module
-- and its hs-boot file
-- Main module: class Eq a => Diag a where
-- diag :: a -> Bool
-- {-# MINIMAL diag #-}
-- Boot file: class Eq a => Diag a
-- The methods do not match: There are different numbers of methods
-- The MINIMAL pragmas are not compatible
```
## Expected behavior
Better error message:
- Don't complain about MINIMAL pragmas that the user has not written.
- Don't complain about different number of methods.
- Rather complain (if necessary) about superfluous superclass constraints.
Conceptually, it is unclear to me why the superclass constraints absolutely __must__ be omitted when the methods are omitted. What is the harm of providing more than the minimal information?
The problem with the current behavior is that it leads the user towards adding _more_ stuff into the boot file when the best solution is to put _less_ stuff. (And you know, you only wanted the banana, but it was in the grip of an ape that held on to a tree whose roots where entangled with the roots of the other trees of the rainforest... :D)
## Environment
* GHC version used: 8.8.1https://gitlab.haskell.org/ghc/ghc/-/issues/14092hs-boot unfolding visibility not consistent between --make and -c2021-11-29T09:15:21ZEdward Z. Yanghs-boot unfolding visibility not consistent between --make and -cduog's comment in https://phabricator.haskell.org/D3815#107812 pointed out an inconsistency between hs-boot handling in --make and -c that I have been dimly aware of for some time now.
Here is how to trigger the situation:
```
-- A.hs-...duog's comment in https://phabricator.haskell.org/D3815#107812 pointed out an inconsistency between hs-boot handling in --make and -c that I have been dimly aware of for some time now.
Here is how to trigger the situation:
```
-- A.hs-boot
module A where
f :: Int -> Int
-- B.hs
module B where
import {-# SOURCE #-} A
-- A.hs
module A where
import B
f :: Int -> Int
f x = x + 1
-- C.hs
module C where
import {-# SOURCE #-} A
g = f 2
```
When we run `ghc-head C.hs --make -O -ddump-simpl -fforce-recomp`, we see that f has been successfully inlined:
```
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
g :: Int
[GblId,
Caf=NoCafRefs,
Str=m,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 20}]
g = GHC.Types.I# 3#
```
However, if we then one-shot compile C.hs, as in `ghc-head -c C.hs -O -ddump-simpl -fforce-recomp`, the unfolding is not seen:
```
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
C.g1 :: Int
[GblId,
Caf=NoCafRefs,
Str=m,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 20}]
C.g1 = GHC.Types.I# 2#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
g :: Int
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 20 0}]
g = f C.g1
```
The crux of the matter is that `--make` and `-c` have different rules about when to make use of the unfolded definition.
The `--make` rule is: compile the modules in some topological order. Any module that comes after `A.hs` sees the improved unfoldings. And as it turns out, the current topological order GHC picks is this:
```
[1 of 4] Compiling A[boot] ( A.hs-boot, A.o-boot )
[2 of 4] Compiling B ( B.hs, B.o )
[3 of 4] Compiling A ( A.hs, A.o )
[4 of 4] Compiling C ( C.hs, C.o )
```
The `-c` rule is more complicated. Every module records a list of transitive module dependencies, and whether or not a boot or non-boot was used. We load an hi-boot file if NONE of the modules we imported "saw" the full hi module, AND we only did direct SOURCE imports. If anyone has transitively imported A.hs, we load the hi file.
In the example above, C.hs ONLY imports A.hs-boot, so hs-boot is obliged to load A.hi-boot, and thus it does not see the unfolding.
The `-c` behavior is the correct behavior, because with the `--make` behavior it is easy to get into a situation where the build is dependent on the topological order chosen. Consider:
- `A.hs-boot`
- `B.hs-boot`
- `A.hs` imports `A.hs-boot`, `B.hs-boot`
- `B.hs` imports `A.hs-boot`, `B.hs-boot`
(Ignore the fact that in GHC today you can't actually directly import your hs-boot file; you can fix this by importing dummy modules.)
Now you can see that depending on the order you compile, e.g., A.hs-boot, B.hs-boot, A.hs, B.hs versus B.hs, A.hs, either A.hs or B.hs will be compiled with the unfoldings for its partner, but not vice versa. This doesn't sound good!
Unfortunately, fixing things properly in `--make` mode is quite troublesome. To understand why, we have to first remind ourself about loop retypechecking in make mode. Remember that GHC knot-ties all of the typechecker data structures together (https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/TyingTheKnot). This means that at the point we typecheck an hs-boot file, we are building an (immutable) graph on top of the impoverished, type signature only declarations from the hs-boot file. When we finish typechecking the loop closer, the only way to "update" all of the old references to the hs-boot file is to throw out the entire graph and rebuild it from scratch (the loop retypecheck!)
So let's think about the A.hs-boot B.hs A.hs C.hs case. At the point we're typechecking A.hs, we throw out the graph referencing A.hs-boot and rebuild it referencing A.hs so that everything gets knot-tied to A.hs. But THEN, C.hs comes around, and it's like, "Oy, I don't want the A.hs version of the graph, I want the A.hs-boot version of the graph." In `-c` mode, this is not a problem, since we have to rebuild the graph from scratch anyway, but in `--make` this is a big deal, since we have to throw everything out and rebuild it AGAIN.
One implementation strategy that doesn't involve mucking about with HPT retypechecking is to somehow make the typechecker aware of what unfoldings it should "see" and which it should not. The idea is that if it can ignore unfoldings that it doesn't have legitimate access to, that should be "just as good" as having typechecked against the hs-boot graph itself. But this sounds very tricky and difficult to get right... so here we are.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.2.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | duog |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"hs-boot unfolding visibility not consistent between --make and -c","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.2.1","keywords":["hs-boot."],"differentials":[],"test_case":"","architecture":"","cc":["duog"],"type":"Bug","description":"duog's comment in https://phabricator.haskell.org/D3815#107812 pointed out an inconsistency between hs-boot handling in --make and -c that I have been dimly aware of for some time now.\r\n\r\nHere is how to trigger the situation:\r\n\r\n{{{\r\n-- A.hs-boot\r\nmodule A where\r\nf :: Int -> Int\r\n\r\n-- B.hs\r\nmodule B where\r\nimport {-# SOURCE #-} A\r\n\r\n-- A.hs\r\nmodule A where\r\nimport B\r\nf :: Int -> Int\r\nf x = x + 1\r\n\r\n-- C.hs\r\nmodule C where\r\nimport {-# SOURCE #-} A\r\ng = f 2\r\n}}}\r\n\r\nWhen we run `ghc-head C.hs --make -O -ddump-simpl -fforce-recomp`, we see that f has been successfully inlined:\r\n\r\n{{{\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\ng :: Int\r\n[GblId,\r\n Caf=NoCafRefs,\r\n Str=m,\r\n Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,\r\n WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 20}]\r\ng = GHC.Types.I# 3#\r\n}}}\r\n\r\nHowever, if we then one-shot compile C.hs, as in `ghc-head -c C.hs -O -ddump-simpl -fforce-recomp`, the unfolding is not seen:\r\n\r\n{{{\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\nC.g1 :: Int\r\n[GblId,\r\n Caf=NoCafRefs,\r\n Str=m,\r\n Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,\r\n WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 20}]\r\nC.g1 = GHC.Types.I# 2#\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\ng :: Int\r\n[GblId,\r\n Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,\r\n WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 20 0}]\r\ng = f C.g1\r\n}}}\r\n\r\nThe crux of the matter is that `--make` and `-c` have different rules about when to make use of the unfolded definition.\r\n\r\nThe `--make` rule is: compile the modules in some topological order. Any module that comes after `A.hs` sees the improved unfoldings. And as it turns out, the current topological order GHC picks is this:\r\n\r\n{{{\r\n[1 of 4] Compiling A[boot] ( A.hs-boot, A.o-boot )\r\n[2 of 4] Compiling B ( B.hs, B.o )\r\n[3 of 4] Compiling A ( A.hs, A.o )\r\n[4 of 4] Compiling C ( C.hs, C.o )\r\n}}}\r\n\r\nThe `-c` rule is more complicated. Every module records a list of transitive module dependencies, and whether or not a boot or non-boot was used. We load an hi-boot file if NONE of the modules we imported \"saw\" the full hi module, AND we only did direct SOURCE imports. If anyone has transitively imported A.hs, we load the hi file.\r\n\r\nIn the example above, C.hs ONLY imports A.hs-boot, so hs-boot is obliged to load A.hi-boot, and thus it does not see the unfolding.\r\n\r\nThe `-c` behavior is the correct behavior, because with the `--make` behavior it is easy to get into a situation where the build is dependent on the topological order chosen. Consider:\r\n\r\n* `A.hs-boot`\r\n* `B.hs-boot`\r\n* `A.hs` imports `A.hs-boot`, `B.hs-boot`\r\n* `B.hs` imports `A.hs-boot`, `B.hs-boot`\r\n\r\n(Ignore the fact that in GHC today you can't actually directly import your hs-boot file; you can fix this by importing dummy modules.)\r\n\r\nNow you can see that depending on the order you compile, e.g., A.hs-boot, B.hs-boot, A.hs, B.hs versus B.hs, A.hs, either A.hs or B.hs will be compiled with the unfoldings for its partner, but not vice versa. This doesn't sound good!\r\n\r\nUnfortunately, fixing things properly in `--make` mode is quite troublesome. To understand why, we have to first remind ourself about loop retypechecking in make mode. Remember that GHC knot-ties all of the typechecker data structures together (https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/TyingTheKnot). This means that at the point we typecheck an hs-boot file, we are building an (immutable) graph on top of the impoverished, type signature only declarations from the hs-boot file. When we finish typechecking the loop closer, the only way to \"update\" all of the old references to the hs-boot file is to throw out the entire graph and rebuild it from scratch (the loop retypecheck!)\r\n\r\nSo let's think about the A.hs-boot B.hs A.hs C.hs case. At the point we're typechecking A.hs, we throw out the graph referencing A.hs-boot and rebuild it referencing A.hs so that everything gets knot-tied to A.hs. But THEN, C.hs comes around, and it's like, \"Oy, I don't want the A.hs version of the graph, I want the A.hs-boot version of the graph.\" In `-c` mode, this is not a problem, since we have to rebuild the graph from scratch anyway, but in `--make` this is a big deal, since we have to throw everything out and rebuild it AGAIN.\r\n\r\nOne implementation strategy that doesn't involve mucking about with HPT retypechecking is to somehow make the typechecker aware of what unfoldings it should \"see\" and which it should not. The idea is that if it can ignore unfoldings that it doesn't have legitimate access to, that should be \"just as good\" as having typechecked against the hs-boot graph itself. But this sounds very tricky and difficult to get right... so here we are.","type_of_failure":"OtherFailure","blocking":[]} -->Divam NarulaDivam Narulahttps://gitlab.haskell.org/ghc/ghc/-/issues/13347Abstract classes in hs-boot should not be treated as injective2019-07-07T18:22:20ZEdward Z. YangAbstract classes in hs-boot should not be treated as injectiveThey could be implemented by a newtype (see `mkClass`), making them non-injective.
Fortunately, this doesn't seem to cause any unsoundness, because the \*constructor\* for the newtype corresponding to a class is never in scope, so we ar...They could be implemented by a newtype (see `mkClass`), making them non-injective.
Fortunately, this doesn't seem to cause any unsoundness, because the \*constructor\* for the newtype corresponding to a class is never in scope, so we are never allowed to unwrap "class newtypes". Phew!
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 8.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | low |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Abstract classes in hs-boot should not be treated as injective","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.1","keywords":["hs-boot"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"They could be implemented by a newtype (see `mkClass`), making them non-injective.\r\n\r\nFortunately, this doesn't seem to cause any unsoundness, because the *constructor* for the newtype corresponding to a class is never in scope, so we are never allowed to unwrap \"class newtypes\". Phew!","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/13322Pattern synonyms in hs-boot files2019-07-07T18:22:27ZEdward Z. YangPattern synonyms in hs-boot filesPattern synonyms are not permitted in hs-boot files:
```
ezyang@sabre:~/bun$ ghc-head -c A.hs-boot
A.hs-boot:3:5: error:
Misplaced pattern synonym signature: pattern X :: Bool
|
3 | pattern X :: Bool
| ^^^^^^^^^^^^^^^^^...Pattern synonyms are not permitted in hs-boot files:
```
ezyang@sabre:~/bun$ ghc-head -c A.hs-boot
A.hs-boot:3:5: error:
Misplaced pattern synonym signature: pattern X :: Bool
|
3 | pattern X :: Bool
| ^^^^^^^^^^^^^^^^^
A.hs-boot:3:13: error:
The pattern synonym signature for ‘X’ lacks an accompanying binding
|
3 | pattern X :: Bool
|
```
There isn't really any reason why they shouldn't be supported.
One thing to check closely: if we bundle a pattern synonym declared in an hs-boot file with another type, in an hs-boot file, what happens? In particular, does an hs file which doesn't bundle the pattern synonym and the type valid?
If we add support for them, make sure that Backpack signatures handle it properly. In particular, suppose we have:
```
signature A(T(X)) where
data T
pattern X :: T
```
Then, when we instantiate A, we must be careful to NOT blindly reuse the implementing module of T for X (as we do today in `uAvailInfo` in `NameShape`), since T and X may be defined in different modules.
I'm not likely to implement this unless someone asks.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 8.1 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | low |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Pattern synonyms in hs-boot files","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.1","keywords":["backpack","hs-boot"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"FeatureRequest","description":"Pattern synonyms are not permitted in hs-boot files:\r\n\r\n{{{\r\nezyang@sabre:~/bun$ ghc-head -c A.hs-boot\r\n\r\nA.hs-boot:3:5: error:\r\n Misplaced pattern synonym signature: pattern X :: Bool\r\n |\r\n3 | pattern X :: Bool\r\n | ^^^^^^^^^^^^^^^^^\r\n\r\nA.hs-boot:3:13: error:\r\n The pattern synonym signature for ‘X’ lacks an accompanying binding\r\n |\r\n3 | pattern X :: Bool\r\n | \r\n}}}\r\n\r\nThere isn't really any reason why they shouldn't be supported.\r\n\r\nOne thing to check closely: if we bundle a pattern synonym declared in an hs-boot file with another type, in an hs-boot file, what happens? In particular, does an hs file which doesn't bundle the pattern synonym and the type valid?\r\n\r\nIf we add support for them, make sure that Backpack signatures handle it properly. In particular, suppose we have:\r\n\r\n{{{\r\nsignature A(T(X)) where\r\n data T\r\n pattern X :: T\r\n}}}\r\n\r\nThen, when we instantiate A, we must be careful to NOT blindly reuse the implementing module of T for X (as we do today in `uAvailInfo` in `NameShape`), since T and X may be defined in different modules.\r\n\r\nI'm not likely to implement this unless someone asks.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/13299Typecheck multiple modules at the same time2021-10-26T12:37:33ZEdward Z. YangTypecheck multiple modules at the same timeangerman asked me to outline how one might go about fixing #1409 (mutually recursive modules without hs-boot). Here is the most recent plan based on #10681 and discussion with SPJ.
- \*The general approach.\*\* Traditionally, users writ...angerman asked me to outline how one might go about fixing #1409 (mutually recursive modules without hs-boot). Here is the most recent plan based on #10681 and discussion with SPJ.
- \*The general approach.\*\* Traditionally, users write hs-boot files, which compile to hi-boot files that are subsequently used for compilation. The approach that SPJ and I would like to take is to replace this step with a new one that typechecks all of the hs files at once.
- \*More details.\*\* Let's suppose we have A.hs and B.hs which import each other, A imports B using a `SOURCE` import, but no B.hs-boot is defined.
We ask GHC to typecheck A.hs and B.hs together to produce hi-boot files for each of the modules. To implement this, we need both a new major mode for this operation (similar to `ghc -c`); and GhcMake needs to be adjusted to call this step on every SCC in the import graph, when one or more modules in the import graph do not have an hs-boot file. This part of the implementation is a bit annoying and was what thwarted me when I've made some stabs at this issue in the past. Probably the easiest thing to do initially is to fix up GhcMake to call your new frontend (you'll put it in `HscMain`) on every SCC. An easy way to check progress here is to get `ghc --make` to print out SCCs before it starts compiling them.
GHC needs to learn how to typecheck multiple modules at the same time. Let's talk a little bit about how typechecking works today: by the time we are at `HscMain` we generally have a `ModSummary` per source module to be compiled. You pass the ModSummary to something like `tcRnModule` and you get back out a `TcGblEnv` containing the results of typechecking. Look at `hscIncrementalCompile`: if you're compiling a module proper, we desugar and optimize it properly (`finish`) and then create an interface for it; if we're only typechecking (`finishTypecheckOnly`) we go straight to generating the interface file after checking.
All of these functions assume, of course, that only one module is being typechecked at a time. So you must break this assumption. This comes in multiple steps. First, the actual typechecking, `tcRnModule` needs to be adjusted. Notice `tcRnModule` takes a single `HsParsedModule`; now you need to feed it multiple parsed modules. You probably want a new function for this? What should this function look like? Trace further into `initTc`: notice that the `TcGblEnv` structure assumes that there is only one module being compiled at a time `tcg_mod` (and other modules). So this assumption needs to be broken.
Now, trace into the main body of typechecking `tcRnModuleTcRnM`. Normally the way we go about doing things is we rename imports, and then we rename and typecheck declarations. Clearly each of your parsed modules needs to have their imports resolved separately; furthermore, they might import each other. This needs to be made to work. I think this will have to be totally reimplemented, because you are going to have to deal with cases like:
```
module A(T) where
import B(T)
module B(T) where
import A(T)
```
This is obviously nonsense and your algorithm needs to identify this and kill it. Once you've done this you should have a separate `tcg_rdr_env` for each of the parsed modules. `tcRnImports` sets up a pile of other variables in `TcGblEnv` too (see bottom) and I'm not sure what should be done with those.
Now we need to rename and typecheck the top-level declarations. Renaming of imported entities should proceed straightforwardly because you set up the GlobalRdrEnv correctly, but you need to give the correct module to each of the top-level declarations. Maybe assume no Template Haskell for now because I have no idea how that's supposed to work. The crux of the matter, though, is that once you've renamed all of the declarations, you now need to compute SCCs over ALL of the modules, because how else are you going to typecheck two mutually recursive declarations over two modules. At this point the one-module assumption of TcGblEnv shouldn't be a problem anymore because when we're dealing with renamed source everything knows its name.
There's a little bit more about the export list (`tcRnExports`) but you've probably already handled this to handle the recursive imports correctly.
Finally, what you'll get out in the end is a big pile of types from DIFFERENT modules `tcg_type_env` (and all of the other things: instances, etc. Though, I guess if an instance is defined in one module of a recursive module loop, it should be in scope everywhere?!) So now in the final stage, serializing to interface files, we need to disentangle everything and put the declarations for each module into a separate interface file per module. Maybe best to have kept them separate to begin with.
To conclude, adding support for typechecking multiple modules at once will probably involve rewriting large swathes of the renamer and top-level typechecking driver, but everything past that should basically be unchanged.https://gitlab.haskell.org/ghc/ghc/-/issues/13180Confusing error when hs-boot abstract data implemented using synonym2019-07-07T18:23:18ZEdward Z. YangConfusing error when hs-boot abstract data implemented using synonymThis example is a little goofy (that's why I've marked it low priority), but here goes:
```
-- A.hs-boot
module A where
data T
f :: T -> T
-- B.hs
module B(f) where
import {-# SOURCE #-} A
-- A.hs
module A(T, f) where
import quali...This example is a little goofy (that's why I've marked it low priority), but here goes:
```
-- A.hs-boot
module A where
data T
f :: T -> T
-- B.hs
module B(f) where
import {-# SOURCE #-} A
-- A.hs
module A(T, f) where
import qualified B
type T = Int
f :: T -> T
f x = B.f x
```
When compiled, we get a very interesting error:
```
ezyang@sabre:~$ ghc-head --make A.hs -fforce-recomp
[1 of 3] Compiling A[boot] ( A.hs-boot, A.o-boot )
[2 of 3] Compiling B ( B.hs, B.o )
[3 of 3] Compiling A ( A.hs, A.o )
A.hs-boot:2:1: error:
Type constructor ‘T’ has conflicting definitions in the module
and its hs-boot file
Main module: type T = Int
Boot file: abstract T
|
2 | data T
| ^^^^^^
A.hs-boot:3:1: error:
Identifier ‘f’ has conflicting definitions in the module
and its hs-boot file
Main module: f :: T -> T
Boot file: f :: T -> T
The two types are different
|
3 | f :: T -> T
| ^^^^^^^^^^^
```
The first error is legitimate, but the second is puzzling. I believe the problem has to do with the fact that when we load the hs-boot file for checking, we knot-tie it against itself. Thus, the T referenced in "Boot file" is the one WITHOUT the type synonym unfolding, while the main module \*does\* have the type synonym unfolding.
Pretty-printing is horribly confusing in these situations, so it would be nice if we could give some better guidance here.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 8.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | low |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Confusing error when hs-boot abstract data implemented using synonym","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.1","keywords":["hs-boot"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"This example is a little goofy (that's why I've marked it low priority), but here goes:\r\n\r\n{{{\r\n-- A.hs-boot\r\nmodule A where\r\ndata T\r\nf :: T -> T\r\n\r\n-- B.hs\r\nmodule B(f) where\r\nimport {-# SOURCE #-} A\r\n\r\n-- A.hs\r\nmodule A(T, f) where\r\n import qualified B\r\n type T = Int\r\n f :: T -> T\r\n f x = B.f x\r\n}}}\r\n\r\nWhen compiled, we get a very interesting error:\r\n\r\n{{{\r\nezyang@sabre:~$ ghc-head --make A.hs -fforce-recomp\r\n[1 of 3] Compiling A[boot] ( A.hs-boot, A.o-boot )\r\n[2 of 3] Compiling B ( B.hs, B.o )\r\n[3 of 3] Compiling A ( A.hs, A.o )\r\n\r\nA.hs-boot:2:1: error:\r\n Type constructor ‘T’ has conflicting definitions in the module\r\n and its hs-boot file\r\n Main module: type T = Int\r\n Boot file: abstract T\r\n |\r\n2 | data T\r\n | ^^^^^^\r\n\r\nA.hs-boot:3:1: error:\r\n Identifier ‘f’ has conflicting definitions in the module\r\n and its hs-boot file\r\n Main module: f :: T -> T\r\n Boot file: f :: T -> T\r\n The two types are different\r\n |\r\n3 | f :: T -> T\r\n | ^^^^^^^^^^^\r\n}}}\r\n\r\nThe first error is legitimate, but the second is puzzling. I believe the problem has to do with the fact that when we load the hs-boot file for checking, we knot-tie it against itself. Thus, the T referenced in \"Boot file\" is the one WITHOUT the type synonym unfolding, while the main module *does* have the type synonym unfolding.\r\n\r\nPretty-printing is horribly confusing in these situations, so it would be nice if we could give some better guidance here.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/13069hs-boot files permit default methods in type class (but don't typecheck them)2020-01-23T19:32:33ZEdward Z. Yanghs-boot files permit default methods in type class (but don't typecheck them)The documentation claims you are not allowed to do so but the compiler does not reject it.
```
ezyang@sabre:~$ cat A.hs-boot
module A where
class C a where
f :: a -> a
f x = x
ezyang@sabre:~$ ghc-head -c A.hs-boot -fforce-recomp...The documentation claims you are not allowed to do so but the compiler does not reject it.
```
ezyang@sabre:~$ cat A.hs-boot
module A where
class C a where
f :: a -> a
f x = x
ezyang@sabre:~$ ghc-head -c A.hs-boot -fforce-recomp
ezyang@sabre:~$ ghc-head --version
The Glorious Glasgow Haskell Compilation System, version 8.1.20161218
```
The same problem applies to hsig files (which combust more spectacularly)
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 8.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"hs-boot files permit default methods in type class","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.1","keywords":["backpack","hs-boot"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"The documentation claims you are not allowed to do so but the compiler does not reject it.\r\n\r\n{{{\r\nezyang@sabre:~$ cat A.hs-boot\r\nmodule A where\r\nclass C a where\r\n f :: a -> a\r\n f x = x\r\nezyang@sabre:~$ ghc-head -c A.hs-boot -fforce-recomp\r\nezyang@sabre:~$ ghc-head --version\r\nThe Glorious Glasgow Haskell Compilation System, version 8.1.20161218\r\n}}}\r\n\r\nThe same problem applies to hsig files (which combust more spectacularly)","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/12063Knot-tying failure when type-synonym refers to non-existent data2019-07-07T18:27:48ZEdward Z. YangKnot-tying failure when type-synonym refers to non-existent dataConsider:
```
-- A.hs-boot
module A
data T
-- B.hs
module B
import {-# SOURCE #-} A
type S = T
-- A.hs
module A
import B
x :: S
x = undefined
```
This will cause a tcIfaceGlobal panic when compiling `A.hs` in one-shot mode. The reaso...Consider:
```
-- A.hs-boot
module A
data T
-- B.hs
module B
import {-# SOURCE #-} A
type S = T
-- A.hs
module A
import B
x :: S
x = undefined
```
This will cause a tcIfaceGlobal panic when compiling `A.hs` in one-shot mode. The reason is that we will attempt to knot tie `B.hi`'s reference to `A:T` when typechecking the contents of `A.hs`, but unfortunately there is no declaration of `T` in the real `A.hs`, so the knot tying will fail.
It's not entirely clear to me how to deal with this problem. The obvious thing to do is to fault in the `TyThing` from the ModDetails of the hs-boot file (great care must be taken because we don't actually want to load the hs-boot file!) We'll then fail in due course when we check if we actually implement everything the hs-boot file needs.
But this could complicate things:
1. This "robustness" could mask another, much more difficult to diagnose bug when there was an actual knot-tying failure. Then, we will silently fail to tie the knot, and then if something bad happens it is a lot less obvious what went wrong. However! I think there is an ASSERT we can add in this case: when we add things to the type environment (and update the knot tying variable), we can check if we have faulted in the relevant entity from the hs-boot file. If we have, then we know there is knot-tying failure and we should fail.
1. Having two sets of `TyThing`s for the same `Name` could lead to hard to diagnose errors; e.g., you are comparing `S` for equality with `S`, but they return not equal because one of these is a type synonym tycon and the other is an abstract tycon. There might be some adjustments to the debug printing mechanism to make such situations more obvious.
Here is another way to fix the bug: check if all the types declared in the hs-boot file are actually defined before doing any typechecking proper. Interestingly, this is a possible solution for #12034: the implication is that all types mentioned in the hs-boot file must be defined PRIOR to the first splice. If we look at #1012, this would not be a burden for users that use TH and hs-boot. Also, it nicely fits with a solution for #12042, since we need to check for type synonym loops at some point BEFORE typechecking, and this is the obvious place to do so.Edward Z. YangEdward Z. Yang