GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2023-08-11T07:39:14Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/21575withDict produces incorrect runtime results with optimization enabled2023-08-11T07:39:14ZRyan ScottwithDict produces incorrect runtime results with optimization enabledAfter attempting to change the `reflection` library to use `withDict` instead of `unsafeCoerce` (see #21568 for the motivation) using GHC 9.4.1-alpha1, `reflection`'s test suite started to error out. Here is a minimized example of the er...After attempting to change the `reflection` library to use `withDict` instead of `unsafeCoerce` (see #21568 for the motivation) using GHC 9.4.1-alpha1, `reflection`'s test suite started to error out. Here is a minimized example of the error:
```hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
#define WITH_DICT 1
module Main (main) where
import Control.Monad (unless)
import qualified Data.Map as M
import Data.Map (Map)
#if WITH_DICT
import GHC.Exts (withDict)
#else
import Unsafe.Coerce (unsafeCoerce)
#endif
main :: IO ()
main = do
testCase (give Normal (toJSON (Foo Bar)))
(Object (M.fromList [("Foo",String "Bar")]))
testCase (give ViaShow (toJSON (Foo Bar)))
(Object (M.fromList [("Foo",String "SHOWBAR")]))
putStrLn "All tests passed!"
-----
testCase :: (Eq a, Show a) => a -> a -> IO ()
testCase expected actual =
unless (expected == actual) $
error $ unlines
[ ""
, "Expected: " ++ show expected
, "Actual: " ++ show actual
]
class Given a where
given :: a
give :: forall a r. a -> (Given a => r) -> r
#if WITH_DICT
give = withDict @a @(Given a)
#else
give a k = unsafeCoerce (Gift k :: Gift a r) a
newtype Gift a r = Gift (Given a => r)
#endif
data Foo = Foo Bar
instance Show Foo where
show _ = "SHOWFOO"
data Bar = Bar | BarBar
instance Show Bar where
show _ = "SHOWBAR"
----------------------------------------------------------------------------
-- ToJSON instances
----------------------------------------------------------------------------
instance Given Style => ToJSON Foo where
toJSON (Foo x) = Object $ M.singleton "Foo" (toJSON x)
instance Given Style => ToJSON Bar where
toJSON x = case given of
Normal -> String $ case x of
Bar -> "Bar"
BarBar -> "BarBar"
ViaShow -> String $ show x
data Style = Normal | ViaShow
----------------------------------------------------------------------------
-- Minimized aeson
----------------------------------------------------------------------------
class ToJSON a where
toJSON :: a -> Value
data Value
= Object !(Map String Value)
| String !String
deriving (Eq, Show)
```
If you compile this with GHC 9.4.1-alpha1 plus optimization, it will unexpectedly fail:
```
$ ~/Software/ghc-9.4.0.20220501/bin/ghc Bug.hs -O -fforce-recomp
[1 of 2] Compiling Main ( Bug.hs, Bug.o )
[2 of 2] Linking Bug [Objects changed]
$ ./Bug
Bug:
Expected: Object (fromList [("Foo",String "Bar")])
Actual: Object (fromList [("Foo",String "SHOWBAR")])
CallStack (from HasCallStack):
error, called at Bug.hs:33:5 in main:Main
```
On the other hand, if you compile it without optimization, it succeeds:
```
$ ~/Software/ghc-9.4.0.20220501/bin/ghc Bug.hs -O0 -fforce-recomp
[1 of 2] Compiling Main ( Bug.hs, Bug.o )
[2 of 2] Linking Bug [Objects changed]
$ ./Bug
All tests passed!
```
This behavior appears to be exclusive to `withDict`. If I define `give` in terms of `unsafeCoerce` (which is how `reflection` currently implements `give`), then it works regardless of the optimization level:
```
$ sed -i 's/#define WITH_DICT 1/#define WITH_DICT 0/' Bug.hs
$ ~/Software/ghc-9.4.0.20220501/bin/ghc Bug.hs -O -fforce-recomp
[1 of 2] Compiling Main ( Bug.hs, Bug.o )
[2 of 2] Linking Bug [Objects changed]
$ ./Bug
All tests passed!
$ ~/Software/ghc-9.4.0.20220501/bin/ghc Bug.hs -O0 -fforce-recomp
[1 of 2] Compiling Main ( Bug.hs, Bug.o )
[2 of 2] Linking Bug [Objects changed]
$ ./Bug
All tests passed!
```9.4.1Simon Peyton JonesSimon Peyton Joneshttps://gitlab.haskell.org/ghc/ghc/-/issues/19841Debug.Trace.trace optimized away when throwing exceptions, starting with GHC 8.82023-07-20T07:17:29ZAndreas AbelDebug.Trace.trace optimized away when throwing exceptions, starting with GHC 8.8## Summary
`Debug.Trace.trace` messages printed with `-O0` are optimized away (e.g. with `-O1`) in the presence of `Control.Exception.throw`, starting with GHC 8.8.
## Steps to reproduce
Compile and run this with either `-O0` or `-O1`...## Summary
`Debug.Trace.trace` messages printed with `-O0` are optimized away (e.g. with `-O1`) in the presence of `Control.Exception.throw`, starting with GHC 8.8.
## Steps to reproduce
Compile and run this with either `-O0` or `-O1`:
```haskell
import Control.Exception
import Debug.Trace
data MyException = MyException deriving Show
instance Exception MyException
main :: IO ()
main = handle (\ MyException -> putStrLn "Exception") $ do
trace "Debugging" 5
`seq` throw MyException
`seq` putStrLn "Survived Exception"
```
## Expected behavior
I expect the output
```
Debugging
Exception
```
which is delivered with `-O0` and, up to GHC 8.6, also with `-O1`.
Starting with GHC 8.8 at `-O1`, it prints only
```
Exception
```
## Further research
My mental model for `Debug.Trace.trace :: String -> a -> a` is one of
```haskell
trace msg a = unsafePerformIO (putStrLn msg) `seq` a
trace msg a = unsafePerformIO $ do
putStrLn msg
return a
```
Both of these actually work, meaning that the debug message is **not** optimized away for these simple implementations.
Trivium: I found this bug (as I would call it) when researching https://github.com/agda/agda/issues/5379.
## Environment
* GHC versions used: 8.0.2, 8.2.2, 8.4.4, 8.6.4, 8.8.4, 8.10.4, 9.0.1
Optional:
* Operating System: macOS Mojave 10.14.6
* System Architecture: Macky McMacFacehttps://gitlab.haskell.org/ghc/ghc/-/issues/14901dsrun014 fails with most ways2023-06-30T18:38:57ZAlp Mestanogullaridsrun014 fails with most waysThe `dsrun014` test doesn't seem to pass for a whole bunch of ways, as a recent `./validate --slow` (against yesterday's master) revealed.
```py
# the test options
test('dsrun014', normal, compile_and_run, ['-fobject-code'])
```
```hs
...The `dsrun014` test doesn't seem to pass for a whole bunch of ways, as a recent `./validate --slow` (against yesterday's master) revealed.
```py
# the test options
test('dsrun014', normal, compile_and_run, ['-fobject-code'])
```
```hs
-- the haskell program we build & run
{-# LANGUAGE UnboxedTuples #-}
module Main where
import Debug.Trace
{-# NOINLINE f #-}
f :: a -> b -> (# a,b #)
f x y = x `seq` y `seq` (# x,y #)
g :: Int -> Int -> Int
g v w = case f v w of
(# a,b #) -> a+b
main = print (g (trace "one" 1) (trace "two" 2))
-- The args should be evaluated in the right order!
```
```
# the failing ways
/tmp/ghctest-n4fi8zlk/test spaces/./deSugar/should_run/dsrun014.run dsrun014 [bad stderr] (hpc)
/tmp/ghctest-n4fi8zlk/test spaces/./deSugar/should_run/dsrun014.run dsrun014 [bad stderr] (optasm)
/tmp/ghctest-n4fi8zlk/test spaces/./deSugar/should_run/dsrun014.run dsrun014 [bad stderr] (threaded2)
/tmp/ghctest-n4fi8zlk/test spaces/./deSugar/should_run/dsrun014.run dsrun014 [bad stderr] (dyn)
/tmp/ghctest-n4fi8zlk/test spaces/./deSugar/should_run/dsrun014.run dsrun014 [bad stderr] (optllvm)
```
With those 5 ways, the program's trace is `two` then `one` while with some other ways (like ghci or normal) we get (as expected by the testsuite) `one` first and `two` afterwards.
I'm not sure whether the expectation is too strong or whether there's something fishy going on with those 5 ways.
Simon, could you perhaps comment on this? Is this a "proper" bug?https://gitlab.haskell.org/ghc/ghc/-/issues/23455Binder-swap for singletons looks fishy2023-06-15T17:00:11ZAdam GundryBinder-swap for singletons looks fishyReading #21229 and `Note [Care with binder-swap on dictionaries]`, I wondered if there might not be a similar issue with singletons such as `SNat` as there is with dictionaries. Perhaps this is related to #23109?
Imagine we had this:
``...Reading #21229 and `Note [Care with binder-swap on dictionaries]`, I wondered if there might not be a similar issue with singletons such as `SNat` as there is with dictionaries. Perhaps this is related to #23109?
Imagine we had this:
```hs
f :: forall (a :: Natural) . SNat a -> blah
h = \ @(a :: Natural) (snat :: SNat a)
let co = SNat[0] <a> :: SNat a ~R# Natural
case snat |> co of wild
0 -> f @0 (0 |> sym (SNat[0] <0>))
1 -> f @a snat
```
Now we can binder-swap (`snat` is not a dictionary Id) and unfold `wild` to get
```hs
h = \ @(a :: Natural) (snat :: SNat a)
let co = SNat[0] <a> :: SNat a ~R# Natural
case snat |> co of wild
0 -> f @0 (0 |> sym (SNat[0] <0>))
1 -> f @a (1 |> sym co)
```
Unlike the example from the Note, we do not directly specialise `f @a d` to `f @a (1 |> sym co)` because the argument is not a dictionary. But `f @a (1 |> sym co)` still seems problematic, because it is valid only within the body of the case (outside of the case we have no reason to believe that the `a = 1`), but there is nothing to stop it floating.
Here's a more concrete example:
```hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
module SNatBinderSwap where
import Data.Kind (Type)
import Data.Type.Equality
import GHC.TypeNats
type G :: Nat -> Type -> Type -> Type
type family G n s t where
G 0 s _ = s
G _ _ t = t
newtype N n s t = MkN { unN :: G n s t }
{-# NOINLINE f #-}
f :: SNat a -> N a Bool Char
f x | Just Refl <- testEquality x (SNat @0) = MkN True
| Just Refl <- testEquality x (SNat @1) = MkN 'x'
h :: SNat a -> N a Bool Char
h snat
| Just Refl <- testEquality snat (SNat @1) = f snat
| Just Refl <- testEquality snat (SNat @0) = f snat
```
Compiling this with 9.6.1 gives:
```hs
h2 :: Natural
h2 = NS 0##
h1 :: forall {a :: Nat}. N a Bool Char
h1
= \ (@(a_aHj :: Nat)) ->
f @a_aHj (h2 `cast` <Co:3> :: Natural ~R# SNat a_aHj)
h :: forall (a :: Nat). SNat a -> N a Bool Char
h = \ (@(a_aHj :: Nat)) (snat_X1 :: SNat a_aHj) ->
case snat_X1 `cast` <Co:2> :: SNat a_aHj ~R# Natural of wild_aJ1 {
NS x1_aJ2 ->
case x1_aJ2 of {
__DEFAULT -> case h3 of wild2_00 { };
0## -> h1 @a_aHj;
1## -> f @a_aHj (wild_aJ1 `cast` <Co:3> :: Natural ~R# SNat a_aHj)
};
NB x1_aJa -> case h3 of wild1_00 { }
}
```
Now `h1` looks fishy. It claims to have type `N a Bool Char`, but its implementation passes 0 as the evidence for `SNat a`. Hence it will always evaluate to `MkN True`, and if we were to somehow arrange to call `h1` at some non-zero `Nat`, we would have a type soundness bug.
I haven't yet managed to construct an example that actually exploits this. But is there some principled reason why it is safe, or are we just relying on being lucky?
Perhaps the underlying issue here is that `SNat` is not a "real" singleton type, so we don't have a way to pattern-match on it and keep track of the evidence that we have done so.Matthew PickeringMatthew Pickeringhttps://gitlab.haskell.org/ghc/ghc/-/issues/23508Heap profiler doesn't collect samples when process is idle2023-06-11T12:50:51ZBen GamariHeap profiler doesn't collect samples when process is idleConsider this program which runs for 10 seconds, mostly blocked in a safe foreign call:
```haskell
-- hi.hs
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Types
import Control.Concurrent
import Control.Monad
foreign import cc...Consider this program which runs for 10 seconds, mostly blocked in a safe foreign call:
```haskell
-- hi.hs
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Types
import Control.Concurrent
import Control.Monad
foreign import ccall "sleep" c_sleep :: CInt -> IO ()
force_activity = False
main = do
putStrLn "Hello"
when force_activity $ void $ forkIO $ forever $ threadDelay 1000
c_sleep 10
putStrLn "World"
```
When running with a 10ms sample period, we would expect 1000 samples. However, in practice we see only three:
```
$ ghc -threaded hi.hs -rtsopts -prof -eventlog
$ time ./hi +RTS -l -i0.01 -p -hc
Hello
World
real 0m10.013s
user 0m0.041s
sys 0m0.172s
$ ghc-events show /home/ben/hi.eventlog | grep 'start heap prof sample'
304981792: start heap prof sample 0
606904028: start heap prof sample 0
10003624192: start heap prof sample 0
```
This could be considered either a bug or a feature: since there is no activity there is no allocation and consequently the census should be unchanged. However, this behavior can be somewhat surprising.
One might think that setting `force_activity=True` would help as it would spur periodic activity. However, even then the number of samples is quite a bit smaller than expected:
```
$ nix run .#ghc-events -- show /home/ben/hi.eventlog | grep 'start heap prof sample' | wc -l
41
```
Brought to my attention by @maralorn.https://gitlab.haskell.org/ghc/ghc/-/issues/15975semantics of concurrent program depends on -O level, -f[no-]omit-yields2023-05-09T23:14:05Zwaldmann@imn.htwk-leipzig.desemantics of concurrent program depends on -O level, -f[no-]omit-yieldsI am surprised by the behaviour of the program below
(the interesting property is whether it will output "foo").
Behaviours (plural) actually: it seems to depend
on optimisation level, on omit-yields,
and on very small changes in the so...I am surprised by the behaviour of the program below
(the interesting property is whether it will output "foo").
Behaviours (plural) actually: it seems to depend
on optimisation level, on omit-yields,
and on very small changes in the source code:
It does print (mostly), when compiled with -O0.
It does not, when compiled with -O2.
With -O2 -fno-omit-yields, it will print.
With -O0 -fno-omit-yields, and when I remove the two newTVar
in the beginning, it will mostly not print.
How come?
These differences already occur with the
last two lines replaced by "forever $ return ()",
so the STM stuff may be inessential here.
But that's the context where I came across the problem.
See also discussion at https://mail.haskell.org/pipermail/haskell-cafe/2018-November/130274.html
```
import Control.Concurrent.STM
import Control.Concurrent ( forkIO )
import Control.Monad ( forever )
import System.IO
main = do
atomically $ newTVar "bar"
atomically $ newTVar False
forkIO $ putStrLn "foo"
x <- atomically $ newTVar False
forever $ atomically $ writeTVar x True
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 8.6.2 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Runtime System |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"semantics of concurrent program depends on -O level, -f[no-]omit-yields","status":"New","operating_system":"","component":"Runtime System","related":[],"milestone":"Research needed","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.2","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"I am surprised by the behaviour of the program below\r\n(the interesting property is whether it will output \"foo\").\r\n\r\nBehaviours (plural) actually: it seems to depend\r\non optimisation level, on omit-yields,\r\nand on very small changes in the source code:\r\n\r\nIt does print (mostly), when compiled with -O0.\r\nIt does not, when compiled with -O2.\r\nWith -O2 -fno-omit-yields, it will print.\r\nWith -O0 -fno-omit-yields, and when I remove the two newTVar\r\nin the beginning, it will mostly not print.\r\n\r\nHow come?\r\n\r\nThese differences already occur with the\r\nlast two lines replaced by \"forever $ return ()\",\r\nso the STM stuff may be inessential here.\r\nBut that's the context where I came across the problem.\r\n\r\n\r\nSee also discussion at https://mail.haskell.org/pipermail/haskell-cafe/2018-November/130274.html\r\n\r\n\r\n{{{\r\nimport Control.Concurrent.STM\r\nimport Control.Concurrent ( forkIO )\r\nimport Control.Monad ( forever )\r\nimport System.IO\r\n\r\nmain = do\r\n\r\n atomically $ newTVar \"bar\"\r\n atomically $ newTVar False\r\n\r\n forkIO $ putStrLn \"foo\"\r\n\r\n x <- atomically $ newTVar False\r\n forever $ atomically $ writeTVar x True\r\n}}}\r\n","type_of_failure":"OtherFailure","blocking":[]} -->Research neededhttps://gitlab.haskell.org/ghc/ghc/-/issues/23069Windows: Inconsistent hReady behavior between POSIX and native IO managers2023-03-07T15:01:07ZRyan ScottWindows: Inconsistent hReady behavior between POSIX and native IO managersWhile debugging a Windows-only issue with the `sbv` library (see https://github.com/LeventErkok/sbv/issues/644), it was discovered that the `hReady` function behaves inconsistently between IO managers on Windows. The following two progra...While debugging a Windows-only issue with the `sbv` library (see https://github.com/LeventErkok/sbv/issues/644), it was discovered that the `hReady` function behaves inconsistently between IO managers on Windows. The following two programs attempt to minimize what goes on when `sbv` communicates with an external process:
```hs
-- Producer.hs
module Main where
import System.IO
main :: IO ()
main = do
s <- getLine
putStrLn $ "Responding to: " ++ s
hFlush stdout
_ <- getLine
pure ()
```
```hs
-- Consumer.hs
module Main (main) where
import Control.Concurrent
import System.Environment
import System.Process
import System.IO
main :: IO ()
main = do
args <- getArgs
pgm <- case args of
[p] -> pure p
_ -> error "Pass the path to the producer executable"
(inp, out, _, _) <- runInteractiveProcess pgm [] Nothing Nothing
hPutStrLn inp "Hello"
hFlush inp
threadDelay 1000000
let collect = reverse <$> go ""
go sofar = do isReady <- hReady out
if isReady
then do c <- hGetChar out
go (c : sofar)
else pure sofar
s <- collect
putStrLn $ "Producer says: " ++ s
```
First, compile these programs with:
```
$ ghc-9.2.7 -fforce-recomp Producer.hs -rtsopts
$ ghc-9.2.7 -fforce-recomp Consumer.hs -rtsopts
```
Next, run the following command, first using the POSIX IO manager:
```
$ ./Consumer.exe ./Producer.exe +RTS --io-manager=posix -RTS
Producer says: Responding to: Hello
```
This is the expected result, and this is what happens on Mac and Linux as well.
Now run the same command, but with the native IO manager:
```
$ ./Consumer.exe ./Producer.exe +RTS --io-manager=native -RTS
Producer says:
```
This time, it simply prints out `Producer says:` without any further output. This seems to indicate that the behavior of `hReady` differs with the IO manager, which is not what I would expect.
I used GHC 9.2.7 above, but I get the same results with 9.4.4 and 9.6.1-alpha3 as well.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/18444GHC does not respect semantics of bang patterns2023-01-15T10:52:48ZRichard Eisenbergrae@richarde.devGHC does not respect semantics of bang patternsIf I write
```hs
a :: forall a. a -> a
a = let y :: Eq a => a
!y = undefined
in \z -> z
```
and evaluate `a True`, I get `True`. This is surprising -- I expected to see an exception thrown. The problem is that GHC does not ...If I write
```hs
a :: forall a. a -> a
a = let y :: Eq a => a
!y = undefined
in \z -> z
```
and evaluate `a True`, I get `True`. This is surprising -- I expected to see an exception thrown. The problem is that GHC does not instantiate `y` before forcing it. `y` is a function from `Eq a` to `a`, and thus is a value.
Yet https://gitlab.haskell.org/haskell/prime/-/wikis/BangPatterns describes the desugaring of the above to be
```hs
a :: forall a. a -> a
a = let y :: Eq a => a
y = undefined
in y `seq` \z -> z
```
which does not compile, as there is no `Eq a` instance. I think we should stick to the specification here and reject both programs. This means that GHC will struggle to support a strict binding of a polymorphic value, but I think that's appropriate, as I would want users to be careful in this corner and say exactly what they mean, lest they get surprised by GHC's behavior.https://gitlab.haskell.org/ghc/ghc/-/issues/5262Compiling with -O makes some expressions too lazy and causes space leaks2022-10-24T16:34:06Zmichal.palkaCompiling with -O makes some expressions too lazy and causes space leaksHere are some expressions that are executed in a too lazy way when optimisation is turned on in GHC. GHC is known to make some expressions too lazy when full laziness is turned on (as in #917), and indeed some of these expressions execut...Here are some expressions that are executed in a too lazy way when optimisation is turned on in GHC. GHC is known to make some expressions too lazy when full laziness is turned on (as in #917), and indeed some of these expressions execute correctly when you add -fno-full-laziness. However, some of them are compiled too lazy even if -fno-full-laziness is present.
Here are terms that get compiled too lazy with -O only when full strictness is on:
```
seq (id (\a -> seq a (\b -> (undefined::Int))) (undefined::Bool))
\a -> seq (seq a (\b -> seq b null) (undefined::([] ([] Int)) -> [] Int)) (\b -> ([]::[] Int)) length
\a -> seq (id (\b -> seq b (\c -> seq)) (length a)) ([]::[] Int)
```
Here are terms which are compiled too lazy with -O regardless of full strictness:
```
seq (seq (odd (undefined::Int)) (\a -> (undefined::[] (Int -> Bool))))
foldr (\a -> seq) id ((:) True (undefined::[] Bool))
\a -> foldr (\b -> \c -> seq c (\d -> ([]::[] Int))) (undefined::Bool -> [] Int) a False
\a -> (\b -> map (seq b id) (seq b ([]::[] Int))) (seq a (\b -> (undefined::([] Bool))))
map (seq (seq (seq 0 (undefined::Bool)) (\a -> \b -> (undefined::Bool)) (undefined::Int)))
map (seq (seq (id (\a -> (undefined::Int)) ([]::[] Int)) (\a -> undefined::Bool)))
\a -> (\b -> (:) (seq b 2) (b (undefined::Int) 0)) (seq a (\b -> (undefined::Int -> [] Int)))
```
To discover the differences, just run the terms (whose types are \[Int\] -\> \[Int\]) on some partially or fully-defined small lists.
It is possible to construct programs which exhibit space leaks only when optimisation is turned on using some of these terms (examples attached).
All programs have been tested with a pre-built GHC 7.1.20110606 on linux x86-64.
Is this a bug? Well, full laziness comes with a disclaimer that some expressions will get too lazy, but this happens even when we turn off full laziness, so it might be a legitimate bug.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------------------ |
| Version | 7.1 |
| Type | Bug |
| TypeOfFailure | IncorrectResultAtRuntime |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Compiling with -O makes some expressions too lazy and causes space leaks","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.1","keywords":["laziness,","leak","space","strictness,"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Here are some expressions that are executed in a too lazy way when optimisation is turned on in GHC. GHC is known to make some expressions too lazy when full laziness is turned on (as in #917), and indeed some of these expressions execute correctly when you add -fno-full-laziness. However, some of them are compiled too lazy even if -fno-full-laziness is present.\r\n\r\nHere are terms that get compiled too lazy with -O only when full strictness is on:\r\n{{{\r\nseq (id (\\a -> seq a (\\b -> (undefined::Int))) (undefined::Bool))\r\n\\a -> seq (seq a (\\b -> seq b null) (undefined::([] ([] Int)) -> [] Int)) (\\b -> ([]::[] Int)) length\r\n\\a -> seq (id (\\b -> seq b (\\c -> seq)) (length a)) ([]::[] Int)\r\n}}}\r\n\r\nHere are terms which are compiled too lazy with -O regardless of full strictness:\r\n{{{\r\nseq (seq (odd (undefined::Int)) (\\a -> (undefined::[] (Int -> Bool))))\r\nfoldr (\\a -> seq) id ((:) True (undefined::[] Bool))\r\n\\a -> foldr (\\b -> \\c -> seq c (\\d -> ([]::[] Int))) (undefined::Bool -> [] Int) a False\r\n\\a -> (\\b -> map (seq b id) (seq b ([]::[] Int))) (seq a (\\b -> (undefined::([] Bool))))\r\nmap (seq (seq (seq 0 (undefined::Bool)) (\\a -> \\b -> (undefined::Bool)) (undefined::Int)))\r\nmap (seq (seq (id (\\a -> (undefined::Int)) ([]::[] Int)) (\\a -> undefined::Bool)))\r\n\\a -> (\\b -> (:) (seq b 2) (b (undefined::Int) 0)) (seq a (\\b -> (undefined::Int -> [] Int)))\r\n}}}\r\n\r\nTo discover the differences, just run the terms (whose types are [Int] -> [Int]) on some partially or fully-defined small lists.\r\n\r\nIt is possible to construct programs which exhibit space leaks only when optimisation is turned on using some of these terms (examples attached).\r\n\r\nAll programs have been tested with a pre-built GHC 7.1.20110606 on linux x86-64.\r\n\r\nIs this a bug? Well, full laziness comes with a disclaimer that some expressions will get too lazy, but this happens even when we turn off full laziness, so it might be a legitimate bug.","type_of_failure":"IncorrectResultAtRuntime","blocking":[]} -->8.0.1https://gitlab.haskell.org/ghc/ghc/-/issues/20745C backend handles signedness incorrectly2022-10-12T13:14:34ZBen GamariC backend handles signedness incorrectlyIn !6934 I noticed and fixed a few issues in the C backend to do with signedness:
### Unsigned operations can sometimes turn into signed operations if given signed arguments
Consider a program like
```c
test() {
bits64 ret,...In !6934 I noticed and fixed a few issues in the C backend to do with signedness:
### Unsigned operations can sometimes turn into signed operations if given signed arguments
Consider a program like
```c
test() {
bits64 ret, a, b;
a = %neg(43 :: bits64);
b = %neg(0x443c70fa3e465120 :: bits64);
ret = %modu(a, b);
return (ret);
}
```
In this case both `a` and `b` will be `StgInt`s in the generated C (since
`MO_Neg` is a signed operation). However, if we provide these as operands to `%` then we
will get *signed* modulus semantics, despite the user requesting *unsigned* semantics.
Therefore we must be careful to cast both arguments to `StgWord`; currently we do not do this.
### Sub-word signed results must be zero-extended
Consider a program like (from #20634):
```c
test() {
bits64 ret;
bits8 a,b;
a = 0xe1 :: bits8; // == -31 signed
b = %quot(a, 3::bits8); // == -10 signed
ret = %zx64(a); // == 0xf6 unsigned
return (ret);
}
```
This program should return `0xf6 == 246`. However, we need to be very careful
with when dealing with the result of the %quot. For instance, one might be
tempted produce code like:
```c
StgWord8 a = 0xe1U;
StgInt8 b = (StgInt8) a / (StgInt8) 0x3U;
StgWord ret = (W_) b;
```
However, this would be wrong; by widening `b` directly from `StgInt8` to
`StgWord` we will get sign-extension semantics: rather than 0xf6 we will get
`0xfffffffffffffff6`. To avoid this we must first cast `b` back to `StgWord8`,
ensuring that we get zero-extension semantics when we widen up to `StgWord`.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/20645LLVM backend miscompiles pext and pdep2022-10-12T13:14:29ZBen GamariLLVM backend miscompiles pext and pdepStrangely, the `W16` and `W8` `pexp` and `pdep` tests of bgamari/test-primops> currently fail with the LLVM backend:
For instance,
```
*Main> let e = parseExpr @W16 "narrow[W64→W16](load[W64](0::W64))"
*Main> let f = parseExpr @W16 "nar...Strangely, the `W16` and `W8` `pexp` and `pdep` tests of bgamari/test-primops> currently fail with the LLVM backend:
For instance,
```
*Main> let e = parseExpr @W16 "narrow[W64→W16](load[W64](0::W64))"
*Main> let f = parseExpr @W16 "narrow[W64→W16]((load[W64](0::W64)) ^ 1::W64)"
*Main> evalCallishOp comp pext (e,f)
2
*Main> evalCallishOp llvm pext (e,f)
0x1e
*Main> interpret e
0x100
*Main> interpret f
0x101
```
The result of this operation should be `2`.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/20638Miscompilation of signed load by ARMv8 NCG2022-10-12T13:14:13ZBen GamariMiscompilation of signed load by ARMv8 NCGThis Cmm program is miscompiled by the ARMv8 NCG.
```c
test(W_ buffer) {
return (%zx64(%shrl(bits16[buffer + (128 :: bits64)], (1 :: bits64))) & (64711 :: bits64));
}
```
With `-fllvm` (and under the bgamari/test-primops> interpreter...This Cmm program is miscompiled by the ARMv8 NCG.
```c
test(W_ buffer) {
return (%zx64(%shrl(bits16[buffer + (128 :: bits64)], (1 :: bits64))) & (64711 :: bits64));
}
```
With `-fllvm` (and under the bgamari/test-primops> interpreter) this evaluates to `0x40c0`. With the NCG it evaluates to `0xc0c0`.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/20637LLVM backend miscompilation2022-10-12T13:14:07ZBen GamariLLVM backend miscompilationThis Cmm program produces different result when compiled using the NCG and LLVM backends:
```c
test() {
return (%zx64(%lobits16(%zx32(%lobits8((%lobits16(%shl((1 :: bits32), (45 :: bits64))) ^ (1 :: bits16)))))));
}
```
The NCG return...This Cmm program produces different result when compiled using the NCG and LLVM backends:
```c
test() {
return (%zx64(%lobits16(%zx32(%lobits8((%lobits16(%shl((1 :: bits32), (45 :: bits64))) ^ (1 :: bits16)))))));
}
```
The NCG returns `1` while LLVM returns `0` with `-O0` and `0x7ff3a69bc010` with `-O1`. I believe `1` is correct.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/20634Inconsistent treatment of signedness of Cmm literals2022-10-12T13:14:02ZBen GamariInconsistent treatment of signedness of Cmm literalsConsider these Cmm programs:
```c
testA() { return ( 0x8000000000000000::bits64 ); }
testB() { return ( -0x8000000000000000::bits64 ); }
testC() { return (%quot( 0x8000000000000000::bits64, 2)); }
testD() { return (%quot...Consider these Cmm programs:
```c
testA() { return ( 0x8000000000000000::bits64 ); }
testB() { return ( -0x8000000000000000::bits64 ); }
testC() { return (%quot( 0x8000000000000000::bits64, 2)); }
testD() { return (%quot(-0x8000000000000000::bits64, 2)); }
```
In general, one wouldn't expect that `0x8000000000000000::bits64` and `-0x8000000000000000::bits64` differ semantically, given that we assume a two's complement sign representation.
In the case of `testA` and `testB` GHC respects this expectation. However, in the case of `testC` and `testD` there is a surprising difference:
```
Program Value emitted in assembler
----------- ---------------------------
`testA` `-9223372036854775808`
`testB` `-9223372036854775808`
`testC` ` 4611686018427387904`
`testD` `-4611686018427387904`
```
That is, while we produce the correct result in `testD`, in `testC` we silently drop the sign bit. Given that this same behavior is not observed in `testA` and `testB`, I can only assume that this is some constant folding going awry.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/18370`BlockedIndefinitelyOnMVar` is thrown to non-blocked threads2022-09-12T00:31:00ZOle`BlockedIndefinitelyOnMVar` is thrown to non-blocked threads## Summary
`BlockedIndefinitelyOnMVar` is thrown to other threads that weren't blocked.
## Steps to reproduce
```haskell
-- Test.hs
{-# LANGUAGE TypeApplications #-}
module Main (main) where
import Control.Concurrent
import Control...## Summary
`BlockedIndefinitelyOnMVar` is thrown to other threads that weren't blocked.
## Steps to reproduce
```haskell
-- Test.hs
{-# LANGUAGE TypeApplications #-}
module Main (main) where
import Control.Concurrent
import Control.Exception
main :: IO ()
main = do
emptyVar <- newEmptyMVar @()
resultVar <- newEmptyMVar
_ <- forkIO $ do
caught <- try @SomeException (readMVar emptyVar)
putMVar resultVar caught
print ("thread", caught)
result <- readMVar resultVar
print ("main", result)
```
```
$ ghc -Wall -Wextra -threaded Test.hs
[1 of 1] Compiling Main ( Test.hs, Test.o )
Linking Test ...
$ ./Test
("thread",Left thread blocked indefinitely in an MVar operation)
Test: thread blocked indefinitely in an MVar operation
$ ./Test +RTS -N
Test: thread blocked indefinitely in an MVar operation
("thread",Left thread blocked indefinitely in an MVar operation)
```
It appears the main thread is being thrown a `BlockedIndefinitelyOnMVar` as well.
This can be decoupled from the empty MVar read:
```haskell
-- Test.hs
{-# LANGUAGE TypeApplications #-}
module Main (main) where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception
main :: IO ()
main = do
emptyVar <- newEmptyTMVarIO @()
resultVar <- newEmptyMVar
_ <- forkIO $ do
caught <- try @SomeException (atomically (readTMVar emptyVar))
putMVar resultVar caught
print ("thread", caught)
result <- readMVar resultVar
print ("main", result)
```
```
$ ghc -Wall -Wextra -threaded Test.hs
[1 of 1] Compiling Main ( Test.hs, Test.o )
Linking Test ...
$ ./Test
("thread",Left thread blocked indefinitely in an STM transaction)
Test: thread blocked indefinitely in an MVar operation
$ ./Test +RTS -N
Test: thread blocked indefinitely in an MVar operation
("thread",Left thread blocked indefinitely in an STM transaction)
```
## Expected behavior
I expect both threads to print the result of `try` in unspecified order.
```
λ ./Test +RTS -N
("thread",Left thread blocked indefinitely in an MVar operation)
("main",Left thread blocked indefinitely in an MVar operation)
```
## Environment
* GHC version used:
- 8.10.1
- 8.8.3
Optional:
* Operating System: NixOS, Linux 5.4.31 x86_64https://gitlab.haskell.org/ghc/ghc/-/issues/22033slow validate failure: IntegerToFloat different result optllvm2022-08-16T12:54:38ZMatthew Pickeringslow validate failure: IntegerToFloat different result optllvmReproduce with
```
hadrian/build test --freeze1 --docs=none --flavour=slow-validate --test-speed=slow --only="IntegerToFloat"
```
```
+++ "/run/user/1000/ghctest-derda1up/test spaces/testsuite/tests/numeric/should_run/IntegerToFloat...Reproduce with
```
hadrian/build test --freeze1 --docs=none --flavour=slow-validate --test-speed=slow --only="IntegerToFloat"
```
```
+++ "/run/user/1000/ghctest-derda1up/test spaces/testsuite/tests/numeric/should_run/IntegerToFloat.run/IntegerToFloat.run.stdout.normalised" 2022-08-05 18:05:17.563600812 +0100
@@ -17,7 +17,7 @@
0x1p36
0x1.fffffcp59
0x1p60
-0x1.fffffep59
+0x1p60
0x1p60
0x1.fffffcp63
0x1p64
*** unexpected failure for IntegerToFloat(optllvm)
```9.4.3Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/8981ghc-pkg complains about missing haddock interface files2022-07-07T15:20:45Zthoughtpoliceghc-pkg complains about missing haddock interface filesAs Conal reported on the mailing list\[1\], `ghc-pkg check` on Mavericks allegedly returns:
```
bash-3.2$ ghc-pkg check
Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/uniplate-1.6.12/html/uniplate.ha...As Conal reported on the mailing list\[1\], `ghc-pkg check` on Mavericks allegedly returns:
```
bash-3.2$ ghc-pkg check
Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/uniplate-1.6.12/html/uniplate.haddock doesn't exist or isn't a file
Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/polyparse-1.9/html/polyparse.haddock doesn't exist or isn't a file
Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/ghc-syb-utils-0.2.1.2/html/ghc-syb-utils.haddock doesn't exist or isn't a file
Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/constraints-0.3.5/html/constraints.haddock doesn't exist or isn't a file
Warning: haddock-html: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/constraints-0.3.5/html doesn't exist or isn't a directory
Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/newtype-0.2/html/newtype.haddock doesn't exist or isn't a file
Warning: haddock-html: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/newtype-0.2/html doesn't exist or isn't a directory
```
It's not fatal, but makes the output much more annoying. I figured this would have been caught by validate or somesuch, but apparently not.
Marking for 7.8.2. I'm looking into this soon.
\[1\]http://www.haskell.org/pipermail/glasgow-haskell-users/2014-April/024846.html
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 7.8.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | Fuuzetsu |
| Operating system | MacOS X |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"ghc-pkg complains about missing haddock interface files","status":"New","operating_system":"MacOS X","component":"Compiler","related":[],"milestone":"7.8.2","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.8.1","keywords":["ghc-pkg"],"differentials":[],"test_case":"","architecture":"","cc":["Fuuzetsu"],"type":"Bug","description":"As Conal reported on the mailing list[1], `ghc-pkg check` on Mavericks allegedly returns:\r\n\r\n{{{\r\n\r\nbash-3.2$ ghc-pkg check\r\n Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/uniplate-1.6.12/html/uniplate.haddock doesn't exist or isn't a file\r\n Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/polyparse-1.9/html/polyparse.haddock doesn't exist or isn't a file\r\n Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/ghc-syb-utils-0.2.1.2/html/ghc-syb-utils.haddock doesn't exist or isn't a file\r\n Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/constraints-0.3.5/html/constraints.haddock doesn't exist or isn't a file\r\n Warning: haddock-html: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/constraints-0.3.5/html doesn't exist or isn't a directory\r\n Warning: haddock-interfaces: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/newtype-0.2/html/newtype.haddock doesn't exist or isn't a file\r\n Warning: haddock-html: /Users/conal/.cabal/share/doc/x86_64-osx-ghc-7.8.1/newtype-0.2/html doesn't exist or isn't a directory\r\n\r\n}}}\r\n\r\nIt's not fatal, but makes the output much more annoying. I figured this would have been caught by validate or somesuch, but apparently not.\r\n\r\nMarking for 7.8.2. I'm looking into this soon.\r\n\r\n[1]http://www.haskell.org/pipermail/glasgow-haskell-users/2014-April/024846.html","type_of_failure":"OtherFailure","blocking":[]} -->8.0.1https://gitlab.haskell.org/ghc/ghc/-/issues/10975At program exit, finalizer runs while foreign function is running2022-04-25T18:50:30Ztakano-akioAt program exit, finalizer runs while foreign function is runningThe following code prints "finalized", which means the finalizer runs while the "call" function is still running.
Steps to reproduce:
```
% ghc finalizer.hs foreign.c -threaded
% ./finalizer
```
finalizer.hs:
```hs
import Control.Con...The following code prints "finalized", which means the finalizer runs while the "call" function is still running.
Steps to reproduce:
```
% ghc finalizer.hs foreign.c -threaded
% ./finalizer
```
finalizer.hs:
```hs
import Control.Concurrent
import Control.Monad
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr
main :: IO ()
main = do
_ <- forkIO $ do
fptr <- newForeignPtr finalizer nullPtr
forever $ withForeignPtr fptr call
threadDelay 1000000
foreign import ccall "&finalizer" finalizer :: FunPtr (Ptr CInt -> IO ())
foreign import ccall "call" call :: Ptr CInt -> IO ()
```
foreign.c
```c
#include <stdio.h>
int finalized = 0;
void finalizer(int *a)
{
finalized = 1;
puts("finalizer");
}
void call(int *a)
{
int i;
unsigned w = 0;
puts("begin call");
for(i = 0; i < 100000000; i++)
{
if(finalized)
puts("finalized");
w += i;
}
printf("end call: %u\n", w);
}
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 7.10.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":"At program exit, finalizer runs while foreign function is running","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.10.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"The following code prints \"finalized\", which means the finalizer runs while the \"call\" function is still running.\r\n\r\nSteps to reproduce:\r\n{{{\r\n% ghc finalizer.hs foreign.c -threaded\r\n% ./finalizer\r\n}}}\r\n\r\nfinalizer.hs:\r\n{{{#!hs\r\nimport Control.Concurrent\r\nimport Control.Monad\r\nimport Foreign.C.Types\r\nimport Foreign.ForeignPtr\r\nimport Foreign.Ptr\r\n\r\nmain :: IO ()\r\nmain = do\r\n _ <- forkIO $ do\r\n fptr <- newForeignPtr finalizer nullPtr\r\n forever $ withForeignPtr fptr call\r\n threadDelay 1000000\r\n\r\nforeign import ccall \"&finalizer\" finalizer :: FunPtr (Ptr CInt -> IO ())\r\nforeign import ccall \"call\" call :: Ptr CInt -> IO ()\r\n}}}\r\n\r\nforeign.c\r\n{{{#!c\r\n#include <stdio.h>\r\n\r\nint finalized = 0;\r\n\r\nvoid finalizer(int *a)\r\n{\r\n finalized = 1;\r\n puts(\"finalizer\");\r\n}\r\n\r\nvoid call(int *a)\r\n{\r\n int i;\r\n unsigned w = 0;\r\n puts(\"begin call\");\r\n for(i = 0; i < 100000000; i++)\r\n {\r\n if(finalized)\r\n puts(\"finalized\");\r\n w += i;\r\n }\r\n printf(\"end call: %u\\n\", w);\r\n}\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/15462fixST for lazy ST is a bit wrong2022-04-12T21:07:34ZDavid FeuerfixST for lazy ST is a bit wrong#15349 exposed a problem with repeated computations in invocations of `fixST` that should fail but can instead produce answers or even subvert safe Haskell. That has been fixed for strict `ST`, but not yet for lazy `ST`.
<details><summa...#15349 exposed a problem with repeated computations in invocations of `fixST` that should fail but can instead produce answers or even subvert safe Haskell. That has been fixed for strict `ST`, but not yet for lazy `ST`.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 8.4.3 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Core Libraries |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"fixST for lazy ST is a bit wrong","status":"New","operating_system":"","component":"Core Libraries","related":[],"milestone":"8.8.1","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.4.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"#15349 exposed a problem with repeated computations in invocations of `fixST` that should fail but can instead produce answers or even subvert safe Haskell. That has been fixed for strict `ST`, but not yet for lazy `ST`.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/14769The RecompBecause [TH] check is not resume-build-safe2022-04-02T17:29:44ZNiklas Hambüchenmail@nh2.meThe RecompBecause [TH] check is not resume-build-safe`ghc --make` aims to make compilation behave correctly and produce up-to-date, no matter whether it completes or is interrupted (e.g. with Ctrl+C) and later resumed
At no point should interrupting a build and running it again produce "l...`ghc --make` aims to make compilation behave correctly and produce up-to-date, no matter whether it completes or is interrupted (e.g. with Ctrl+C) and later resumed
At no point should interrupting a build and running it again produce "less correct" outputs than running it to the end; specifically interrupting shouldn't result in necessary build steps to be "forgotten".
However, this seems to be the case with the `[TH]` check introduced in #481.
Minimal test case with 3 files: https://github.com/nh2/th-recomp-test
Good behaviour when running without interrupting:
```
% touch A.hs && ~/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc --make Main.hs
[1 of 3] Compiling A ( A.hs, A.o )
[2 of 3] Compiling B ( B.hs, B.o ) [TH]
[3 of 3] Compiling Main ( Main.hs, Main.o ) [TH]
Linking Main ...
```
Bad behaviour when interrupting and running again:
```
% touch A.hs; timeout 0.1 ~/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc --make Main.hs; ~/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc --make Main.hs
[1 of 3] Compiling A ( A.hs, A.o )
[2 of 3] Compiling B ( B.hs, B.o ) [TH]
Linking Main ...
```
As you can see, when interrupting, the step `Compiling Main ( Main.hs, Main.o ) [TH]` went missing.
This suggests to me that either:
- the `[TH]`s are unnecessary in the first place (unlikely and I buy into the reason explained by #481 that changed values may be picked up by TH in higher-level modules)
- the resumed `ghc --make` invocation may potentially produce wrong / not-up-to-date outputs (most likely)
- re-running somehow retrieves the information that in fact recompiling `Main` is not necessary for a correct build (if this is the case, then I'd really like to know how that works and we should document it, likely also try to use that way to avoid recompiling `Main` in the uninterrupted run)
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------- |
| Version | 8.2.2 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | nh2, simonmar |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"The RecompBecause [TH] check is not resume-build-safe","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.2.2","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":["nh2","simonmar"],"type":"Bug","description":"`ghc --make` aims to make compilation behave correctly and produce up-to-date, no matter whether it completes or is interrupted (e.g. with Ctrl+C) and later resumed\r\n\r\nAt no point should interrupting a build and running it again produce \"less correct\" outputs than running it to the end; specifically interrupting shouldn't result in necessary build steps to be \"forgotten\".\r\n\r\nHowever, this seems to be the case with the `[TH]` check introduced in #481.\r\n\r\nMinimal test case with 3 files: https://github.com/nh2/th-recomp-test\r\n\r\nGood behaviour when running without interrupting:\r\n\r\n{{{\r\n% touch A.hs && ~/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc --make Main.hs \r\n[1 of 3] Compiling A ( A.hs, A.o )\r\n[2 of 3] Compiling B ( B.hs, B.o ) [TH]\r\n[3 of 3] Compiling Main ( Main.hs, Main.o ) [TH]\r\nLinking Main ...\r\n}}}\r\n\r\nBad behaviour when interrupting and running again:\r\n\r\n{{{\r\n% touch A.hs; timeout 0.1 ~/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc --make Main.hs; ~/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc --make Main.hs\r\n[1 of 3] Compiling A ( A.hs, A.o )\r\n[2 of 3] Compiling B ( B.hs, B.o ) [TH]\r\nLinking Main ...\r\n}}}\r\n\r\nAs you can see, when interrupting, the step `Compiling Main ( Main.hs, Main.o ) [TH]` went missing.\r\n\r\nThis suggests to me that either:\r\n\r\n* the `[TH]`s are unnecessary in the first place (unlikely and I buy into the reason explained by #481 that changed values may be picked up by TH in higher-level modules)\r\n* the resumed `ghc --make` invocation may potentially produce wrong / not-up-to-date outputs (most likely)\r\n* re-running somehow retrieves the information that in fact recompiling `Main` is not necessary for a correct build (if this is the case, then I'd really like to know how that works and we should document it, likely also try to use that way to avoid recompiling `Main` in the uninterrupted run)","type_of_failure":"OtherFailure","blocking":[]} -->