GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2023-12-15T14:50:43Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/23513Restoring a continuation containing a catch# frame can result in incorrect as...2023-12-15T14:50:43ZAlexis KingRestoring a continuation containing a catch# frame can result in incorrect async exception maskingThe following program produces an incorrect result on GHC 9.6.2:
```haskell
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
import Control.Exception
import Data.IORef
import GHC.Prim
import GHC.Types
data PromptTag a = Promp...The following program produces an incorrect result on GHC 9.6.2:
```haskell
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
import Control.Exception
import Data.IORef
import GHC.Prim
import GHC.Types
data PromptTag a = PromptTag (PromptTag# a)
newPromptTag :: IO (PromptTag a)
newPromptTag = IO (\s -> case newPromptTag# s of
(# s', tag #) -> (# s, PromptTag tag #))
prompt :: PromptTag a -> IO a -> IO a
prompt (PromptTag tag) (IO m) = IO (prompt# tag m)
control0 :: PromptTag a -> ((IO b -> IO a) -> IO a) -> IO b
control0 (PromptTag tag) f =
IO (control0# tag (\k -> case f (\(IO a) -> IO (k a)) of IO b -> b))
data E = E deriving (Show)
instance Exception E
main :: IO ()
main = do
tag <- newPromptTag
ref <- newIORef Nothing
mask_ $
prompt tag $
handle (\E -> pure ()) $
control0 tag $ \k ->
writeIORef ref (Just k)
Just k <- readIORef ref
k (throwIO E)
print =<< getMaskingState
```
The program should print `Unmasked`, but it prints `MaskedInterruptible`.
The root cause is an oversight in the implementation of continuation restore. Continuation restore already takes care to patch the restored stack frames when mask/unmask frames are involved, as described in `Note [Continuations and async exception masking]` in `rts/Continuation.c`. However, it turns out that `catch#` frames also store the async exception masking state, so we need to patch those frames in a similar way (but currently do not).9.8.2Alexis KingAlexis Kinghttps://gitlab.haskell.org/ghc/ghc/-/issues/24358For Ratios, round produces incorrect results for some fixed-width signed inte...2024-01-23T17:55:25Zj6careyFor Ratios, round produces incorrect results for some fixed-width signed integer values## Summary
The implementation of "round" for a ratio of fixed-width signed
integers is incorrect when applied to extreme negative values.
## Steps to reproduce
```
GHCi, version 9.8.1: https://www.haskell.org/ghc/ :? for help
ghci> r...## Summary
The implementation of "round" for a ratio of fixed-width signed
integers is incorrect when applied to extreme negative values.
## Steps to reproduce
```
GHCi, version 9.8.1: https://www.haskell.org/ghc/ :? for help
ghci> round ((-128) Data.Ratio.% (-16 :: Integer))
8
ghci> round ((-128) Data.Ratio.% (-16 :: Data.Int.Int8))
-8
```
## Expected behavior
```
GHCi, version 9.8.1: https://www.haskell.org/ghc/ :? for help
ghci> round ((-128) Data.Ratio.% (-16 :: Integer))
8
ghci> round ((-128) Data.Ratio.% (-16 :: Data.Int.Int8))
8
```
## Environment
* GHC version used: 9.8.1, 9.4.7
* Operating System: Ubuntu 22.04.3 LTS
* System Architecture: x86_649.10.1Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/23923Possibly a variant of "Specialiser creates bogus specialisations" (#23109) th...2024-02-29T12:50:14ZMikolaj KonarskiPossibly a variant of "Specialiser creates bogus specialisations" (#23109) that reproduces with -fno-polymorphic-specialisation on GHC 9.8.1-alpha1[Edit: see https://gitlab.haskell.org/ghc/ghc/-/issues/23923#note_524660 for a slightly minimised repro.]
This looks like a repeat of #23109 but even with `-fno-polymorphic-specialisation` that was so far a sufficient workaround for it ...[Edit: see https://gitlab.haskell.org/ghc/ghc/-/issues/23923#note_524660 for a slightly minimised repro.]
This looks like a repeat of #23109 but even with `-fno-polymorphic-specialisation` that was so far a sufficient workaround for it (and is the default on 9.8 right now). It seems to follow the same pattern as with the other related issues: as I tweak the code and GHC options to force specializations to happen, tests errors start appearing and gradually appear in greater number.
Repro: with GHC 9.8.1-alpha1 and https://ghc.gitlab.haskell.org/head.hackage/ run (the -O1 or -O2 is essential; tests pass with -O0):
cabal test simplifiedOnlyTest -ftest_seq --enable-optimization --test-show-details=direct --test-options='-p 3concatBuild1'
on commit
https://github.com/Mikolaj/horde-ad/commit/a9ea6e930f3561d90237cf8a431b8aad24387c8e
The test should succeed, but it fails a runtime check (in an outside library) about ranks that the typing guarantees should agree:
```
3concatBuild1: FAIL
Exception: fromVector: rank mismatch (9,0)
CallStack (from HasCallStack):
error, called at ./Data/Array/Internal/RankedG.hs:152:45 in orthotope-0.1.6.0-479d70a92b172419adf0e1e7dd92abde8158cf52f3d7287680c06fc9bb693f0c:Data.Array.Internal.RankedG
fromVector, called at ./Data/Array/Internal/RankedS.hs:133:21 in orthotope-0.1.6.0-479d70a92b172419adf0e1e7dd92abde8158cf52f3d7287680c06fc9bb693f0c:Data.Array.Internal.RankedS
```
I can't compare with GHC 9.6.2 due to #23922, GHC 9.4.6 fails in exactly the same way (#23866), but it fails on several times more tests (after removing `--test-options`), so the root cause may be different, especially given that GHC 9.4.6 most probably doesn't manage to specialize as much as GHC 9.8.1-alpha1. Adding `-fdicts-cheap -flate-dmd-anal -O2` makes GHC 9.8.1-alpha1 fail on 6 instead of 3 tests. A few performance-improving commits ago it wasn't failing at all (but it was failing similarly with `-fpolymorphic-specialisation` (I don't recall if in exactly the same way)). GHC 9.2.7 is the only recent GHC version that passes this test (and the whole test suite; it compiles quickly and runs quite slowly). IIRC from toying with `inspection-testing`, GHC 9.2.7 specializes even less than the remaining GHCs.9.10.1https://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/15599typeclass inference depends on whether a module is exposed.2019-07-07T18:03:49Zgleachkrtypeclass inference depends on whether a module is exposed.My first bug report for GHC: I've found some strange behavior in GHC 8.2.2 relating to typeclass inference.
Essentially, for two identical modules, GHC infers for one of them that one typeclass instance applies, and for another that a d...My first bug report for GHC: I've found some strange behavior in GHC 8.2.2 relating to typeclass inference.
Essentially, for two identical modules, GHC infers for one of them that one typeclass instance applies, and for another that a different instance applies. The only language extensions involved are FlexibleInstances, MultiParamTypeClasses, GADTs, and ScopedTypeVariables. There are no INCOHERENT pragmas involved. The only difference between the two modules is that one of them (the one displaying correct typeclass inference) is an exposed module, while the other is not mentioned in the cabal file. The phenomenon affects other packages that import the original package---they display the incorrect behavior, rather than the correct behavior that the exposed module displays.
The original discussion is here: [https://github.com/gleachkr/Carnap/issues/4](https://github.com/gleachkr/Carnap/issues/4)
My best attempt at a minimal example can be found at [https://github.com/gleachkr/GHC-Repro](https://github.com/gleachkr/GHC-Repro). You can run the "test.sh" script included there to see the phenomenon in action.
I don't see this behavior in other GHC versions, but I'm told that bug reports for older GHC versions are welcome, so here I am.
<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 | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"typeclass inference depends on whether a module is exposed.","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"8.2.3","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.2.2","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"My first bug report for GHC: I've found some strange behavior in GHC 8.2.2 relating to typeclass inference. \r\n\r\nEssentially, for two identical modules, GHC infers for one of them that one typeclass instance applies, and for another that a different instance applies. The only language extensions involved are FlexibleInstances, MultiParamTypeClasses, GADTs, and ScopedTypeVariables. There are no INCOHERENT pragmas involved. The only difference between the two modules is that one of them (the one displaying correct typeclass inference) is an exposed module, while the other is not mentioned in the cabal file. The phenomenon affects other packages that import the original package---they display the incorrect behavior, rather than the correct behavior that the exposed module displays.\r\n\r\nThe original discussion is here: [https://github.com/gleachkr/Carnap/issues/4]\r\n\r\nMy best attempt at a minimal example can be found at [https://github.com/gleachkr/GHC-Repro]. You can run the \"test.sh\" script included there to see the phenomenon in action.\r\n\r\nI don't see this behavior in other GHC versions, but I'm told that bug reports for older GHC versions are welcome, so here I am.","type_of_failure":"OtherFailure","blocking":[]} -->8.2.3https://gitlab.haskell.org/ghc/ghc/-/issues/15397Linking Issue with libffi on Ubuntu and Fedora with Provided Bindists2024-01-19T11:48:16ZAra AdkinsLinking Issue with libffi on Ubuntu and Fedora with Provided BindistsIt appears that the bindists being given to both ubuntu and fedora users are subtly broken. When attempting to link a binary depending on libffi, the linker picks up the copy of `libffi.so.7` found in the rts folder of the distribution. ...It appears that the bindists being given to both ubuntu and fedora users are subtly broken. When attempting to link a binary depending on libffi, the linker picks up the copy of `libffi.so.7` found in the rts folder of the distribution. This means that at runtime, despite the systems in question having a copy of `libffi.so.6`, the binary can't find the correct shared library to link against.
This does not happen on Arch Linux or Gentoo, as `/usr/lib` or `/usr/lib64` are included in their linker invocations respectively, allowing the linker to pick up the correct version of the dependency.
You can reproduce the issue quickly by cloning [Luna Core](https://github.com/luna/luna-core) and executing the following commands. I suggest doing this on an Ubuntu or Fedora system as I know it fails on those two distros.
```
stack build luna-shell --fast
stack exec luna
```
You should see something along the lines of the following.
```
error while loading shared libraries: libffi.so.7: cannot open shared object file: No such file or directory
```
In essence, the reproduction steps are as follows:
1. Create a project depending on libffi using ghc-8.4.2
1. Build the project
1. Execute it
The *expected* result is that the binary links against the system copy of `libffi.so.6`, rather than the `libffi.so.7` provided in the ghc distribution.
I can't guarantee that Ubuntu and Fedora are the only affected systems, but I know that Arch Linux and Gentoo were both fine with the provided bindists. All tested systems were x64, so I cannot confirm if the issue affects x86 bindists.
The actual symptom appears to be a result of the linker never being given the path to the system library directory (e.g. `/usr/lib` or `/usr/lib64`), whereas on Arch and Gentoo, the linkline does contain that directory.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.4.2 |
| 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":"Linking Issue on Ubuntu and Fedora with Provided Bindists (GHC-8.4.2)","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"8.4.4","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.4.2","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"It appears that the bindists being given to both ubuntu and fedora users are subtly broken. When attempting to link a binary depending on libffi, the linker picks up the copy of `libffi.so.7` found in the rts folder of the distribution. This means that at runtime, despite the systems in question having a copy of `libffi.so.6`, the binary can't find the correct shared library to link against.\r\n\r\nThis does not happen on Arch Linux or Gentoo, as `/usr/lib` or `/usr/lib64` are included in their linker invocations respectively, allowing the linker to pick up the correct version of the dependency.\r\n\r\nYou can reproduce the issue quickly by cloning [https://github.com/luna/luna-core Luna Core] and executing the following commands. I suggest doing this on an Ubuntu or Fedora system as I know it fails on those two distros.\r\n\r\n{{{\r\nstack build luna-shell --fast\r\nstack exec luna\r\n}}}\r\n\r\nYou should see something along the lines of the following. \r\n\r\n{{{\r\nerror while loading shared libraries: libffi.so.7: cannot open shared object file: No such file or directory\r\n}}}\r\n\r\nIn essence, the reproduction steps are as follows:\r\n\r\n1. Create a project depending on libffi using ghc-8.4.2\r\n2. Build the project\r\n3. Execute it\r\n\r\n\r\nThe ''expected'' result is that the binary links against the system copy of `libffi.so.6`, rather than the `libffi.so.7` provided in the ghc distribution. \r\n\r\nI can't guarantee that Ubuntu and Fedora are the only affected systems, but I know that Arch Linux and Gentoo were both fine with the provided bindists. All tested systems were x64, so I cannot confirm if the issue affects x86 bindists. \r\n\r\nThe actual symptom appears to be a result of the linker never being given the path to the system library directory (e.g. `/usr/lib` or `/usr/lib64`), whereas on Arch and Gentoo, the linkline does contain that directory. ","type_of_failure":"OtherFailure","blocking":[]} -->8.4.4https://gitlab.haskell.org/ghc/ghc/-/issues/11261Implement DWARF debugging on powerpc642021-09-07T15:27:56ZPeter Trommlerptrommler@acm.orgImplement DWARF debugging on powerpc64debug:
```
ghc-stage2: panic! (the 'impossible' happened)
(GHC version 7.11.20151219 for powerpc64-unknown-linux):
dwarfReturnRegNo: Unsupported platform!
CallStack (from ImplicitParams):
error, called at compiler/nativeGen/...debug:
```
ghc-stage2: panic! (the 'impossible' happened)
(GHC version 7.11.20151219 for powerpc64-unknown-linux):
dwarfReturnRegNo: Unsupported platform!
CallStack (from ImplicitParams):
error, called at compiler/nativeGen/Dwarf/Constants.hs:224:19 in ghc:Dwarf.Constants
Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug
```
Provide DWARF constants for registers.
Still TODO:
- [ ] add unwinding information to `StgCRun` to ensure that the unwinder can unwind from Haskell into C
- [ ] to support the RTS unwinder: add support to the initial register callback `set_initial_registers` in `rts/Libdw.c`
- [ ] Valid unwind records in `stg_stop_thread` (defined in `rts/StgStartup.cmm`)
- [ ] Support in the native code generator (by implementing the `extractUnwindPoints` field of `NcgImpl`)
- [ ] Unwinding support in `libdw`
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------------ |
| Version | 7.11 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler (CodeGen) |
| Test case | debug, T10667 |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Implement DWARF debugging on powerpc64","status":"New","operating_system":"","component":"Compiler (CodeGen)","related":[],"milestone":"8.2.1","resolution":"Unresolved","owner":{"tag":"OwnedBy","contents":"trommler"},"version":"7.11","keywords":[],"differentials":[],"test_case":"debug, T10667","architecture":"","cc":[""],"type":"Bug","description":"debug:\r\n{{{\r\nghc-stage2: panic! (the 'impossible' happened)\r\n (GHC version 7.11.20151219 for powerpc64-unknown-linux):\r\n dwarfReturnRegNo: Unsupported platform!\r\nCallStack (from ImplicitParams):\r\n error, called at compiler/nativeGen/Dwarf/Constants.hs:224:19 in ghc:Dwarf.Constants\r\n\r\nPlease report this as a GHC bug: http://www.haskell.org/ghc/reportabug\r\n}}}\r\n\r\nProvide DWARF constants for registers.","type_of_failure":"OtherFailure","blocking":[]} -->⊥Peter Trommlerptrommler@acm.orgPeter Trommlerptrommler@acm.orghttps://gitlab.haskell.org/ghc/ghc/-/issues/24505Instances getting incorrect IPE data2024-03-12T15:11:17ZFinley McIlwaineInstances getting incorrect IPE dataCode:
```haskell
{-# LANGUAGE AllowAmbiguousTypes #-}
module Main where
import GHC.InfoProv
import Unsafe.Coerce
-- Boilerplate to help us access the literal dictionaries
data Dict c where
Dict :: forall c. c => Dict c
data Bo...Code:
```haskell
{-# LANGUAGE AllowAmbiguousTypes #-}
module Main where
import GHC.InfoProv
import Unsafe.Coerce
-- Boilerplate to help us access the literal dictionaries
data Dict c where
Dict :: forall c. c => Dict c
data Box where
Box :: forall a. a -> Box
mkBox :: forall a. a => Box
mkBox = unsafeCoerce (Dict @a)
-- Interesting bit
data A = A
instance Eq A where
A == A = True
instance Ord A where
A <= A = undefined
main :: IO ()
main = do
(\(Box d) -> print =<< whereFrom d) $ mkBox @(Eq A)
(\(Box d) -> print =<< whereFrom d) $ mkBox @(Ord A)
```
This program prints the IPE data of the `Eq A` and `Ord A` dictionaries. The `Eq A` dictionary looks as we would expect, with `ipLabel = $fEqA`. The `Ord A` dictionary, however, gets the label `<`. Compile with:
```
ghc -O -fforce-recomp -finfo-table-map -fdistinct-constructor-tables Main.hs
```
The program outputs:
```
Just (InfoProv {ipName = "C:Eq_Main_0_con_info", ipDesc = CONSTR_2_0, ipTyDesc = "Eq", ipLabel = "$fEqA", ipMod = "Main", ipSrcFile = "Main.hs", ipSrcSpan = "23:10-13"})
Just (InfoProv {ipName = "C:Ord_Main_0_con_info", ipDesc = CONSTR, ipTyDesc = "Ord", ipLabel = "<", ipMod = "Main", ipSrcFile = "Main.hs", ipSrcSpan = "26:10-14"})
```
I would expect the `Ord` label to be `$fOrdA`, not `<`.
Interestingly, if we add more explicit definitions to the `Ord A` instance, the label changes. Changing the instance to:
```haskell
instance Ord A where
A <= A = undefined
compare = undefined
```
Changes the label to `>` instead of `<`:
```
Just (InfoProv {ipName = "C:Ord_Main_0_con_info", ipDesc = CONSTR, ipTyDesc = "Ord", ipLabel = ">", ipMod = "Main", ipSrcFile = "Main.hs", ipSrcSpan = "26:10-14"})
```https://gitlab.haskell.org/ghc/ghc/-/issues/24497JS: putStrLn is executed asynchronously in browser2024-03-05T14:47:50ZStefano DebenedettiJS: putStrLn is executed asynchronously in browser## Summary
The `console.log` generated by the JS backend for a `putStrLn` is executed asynchronously in browsers.
## Steps to reproduce
See attached repro case `Test.hs`.
```
$ rm -rf Test Test.hi Test.o Test.jsexe; javascript-unknow...## Summary
The `console.log` generated by the JS backend for a `putStrLn` is executed asynchronously in browsers.
## Steps to reproduce
See attached repro case `Test.hs`.
```
$ rm -rf Test Test.hi Test.o Test.jsexe; javascript-unknown-ghcjs-ghc-9.8.2 -v -dcore-lint Test.hs > ghc.log 2>&1 && ./Test
one
two
```
Loading `Test.jsexe/index.html` is a browser and opening the JS console log will show:
```
two all.js:44450:24
one all.js:8575:17
```
## Expected behavior
The `putStrLn` to be executed synchronously, as in the native backend and as interpreted by `nodejs`.
## Environment
* GHC version used: 9.8.2 JS backend
* Nodejs version used: v20.11.0
* Google Chrome version used: 121.0.6167.184 (Official build) (64 bit)
* Mozilla Firefox version used: 123.0 (64 bit)
Optional:
* Operating System:
* System Architecture:
```
$ uname -a
Linux abcde 6.7.5-gentoo #1 SMP Mon Feb 19 15:51:31 CET 2024 x86_64 Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz GenuineIntel GNU/Linux
```
[Test.hs](/uploads/9c99ee468d81f5bbe52db4dd3ed0ee90/Test.hs)
[ghc.log](/uploads/22b8f9f05f09e7d729fd17499c5b9a41/ghc.log)https://gitlab.haskell.org/ghc/ghc/-/issues/24495Javascript backend -O1 breaks referential transparency in a function having a...2024-03-26T16:52:40ZStefano DebenedettiJavascript backend -O1 breaks referential transparency in a function having a Double argument and doing String concatenation## Summary
A pure `String` value shows up as two different values when output via `putStrLn` and via `console.log`.
The following factors seem to concur in triggering this behaviour:
* The optimization level is `-O1` (does not happen ...## Summary
A pure `String` value shows up as two different values when output via `putStrLn` and via `console.log`.
The following factors seem to concur in triggering this behaviour:
* The optimization level is `-O1` (does not happen with `-O0` or `-O2`)
* The affected function has a `Double` argument (does not happen if the argument is `Int`, see the commented `testInt` function in the repro case)
* The `String` concatenation has certain characteristics (eg. does not happen for the commented `testDouble2` function in the repro case and for other similar tweaks to the `String` concatenation)
## Steps to reproduce
See attached repro case `Test.hs`.
```
$ for i in $(seq 0 2); do rm -rf Test Test.hi Test.o Test.jsexe; echo compiling with -O$i; javascript-unknown-ghcjs-ghc-9.8.2 -v -O$i -dcore-lint Test.hs > ghc.log.O$i 2>&1; echo results with -O$i; ./Test; done
compiling with -O0
results with -O0
1 ab bd
2 ab bd
compiling with -O1
results with -O1
1 ab bd
2 ab
compiling with -O2
results with -O2
1 ab bd
2 ab bd
```
Loading `index.html` in a web browser also exhibits the same behaviour in the JS console log.
## Expected behavior
The program's output to be identical regardless of the optimization level. The second part of the second line of the `-O1` results to be the same as the second part of the first line, i.e. `"ab bd"` instead of just `"ab "`.
## Environment
* GHC version used: 9.8.2 JS backend
* Nodejs version used: v20.11.0
* Google Chrome version used: 121.0.6167.184 (Official build) (64 bit)
Optional:
* Operating System:
* System Architecture:
```
$ uname -a
Linux abcde 6.7.5-gentoo #1 SMP Mon Feb 19 15:51:31 CET 2024 x86_64 Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz GenuineIntel GNU/Linux
```Sylvain HenrySylvain Henryhttps://gitlab.haskell.org/ghc/ghc/-/issues/24450Apparently deadlock in write to closed handle2024-02-18T18:50:57ZBen GamariApparently deadlock in write to closed handle@hvr reported that the following program non-deterministically hang:
```haskell
import System.IO
main :: IO ()
main = do
hPutChar stderr 'A' -- unbuffered
hPutChar stdout 'X' -- buffered -- will indeterministically cause delayed lo...@hvr reported that the following program non-deterministically hang:
```haskell
import System.IO
main :: IO ()
main = do
hPutChar stderr 'A' -- unbuffered
hPutChar stdout 'X' -- buffered -- will indeterministically cause delayed lock-ups
hPutChar stderr 'Z' -- unbuffered
hPutChar stderr '\n' -- unbuffered
```
```sh
$ ghc -threaded -O Main.hs
$ while :; do ./Main >&- ; done
AZ
AZ
*hang*
```
I can confirm that the program hangs almost immediately on Linux with GHC 9.2.8, 9.6.3, and 9.8.1. @hvr says that the issue may have been introduced in GHC 8.2.1.Ben GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/24401fixIO can choke, and report a loop where there is none2024-02-06T15:13:14ZSimon Peyton JonesfixIO can choke, and report a loop where there is noneThis program was written by [Matthew Craven](https://github.com/haskell/core-libraries-committee/issues/242#issuecomment-1907226501):
```
import System.IO( fixIO )
import GHC.Exts (noinline)
main :: IO ()
main = do
r <- fixIO $ \p -> ...This program was written by [Matthew Craven](https://github.com/haskell/core-libraries-committee/issues/242#issuecomment-1907226501):
```
import System.IO( fixIO )
import GHC.Exts (noinline)
main :: IO ()
main = do
r <- fixIO $ \p -> let
{-# NOINLINE q #-}
q = noinline id p
in pure (True : q)
print $! case r of { _:v:_ -> v ; _ -> False }
```
For any law-abiding MonadFix instance, such a call to `mfix` should be semantically equivalent to `pure (repeat True)`. (Specifically, this follows from the 'Purity' law.)
So the `print` should work fine. But actually we get (with -O)
```
run-it: cyclic evaluation in fixIO
```
## Diagnosis
What is going on? Here is the code after optimisation:
```
Main.main1
= \ (s_aRX :: GHC.Prim.State# GHC.Prim.RealWorld) ->
case GHC.Prim.newMVar#
@GHC.Types.Lifted @GHC.Prim.RealWorld @[Bool] s_aRX
of
{ (# ipv_aUk, ipv1_aUl #) ->
case GHC.IO.Unsafe.unsafeDupableInterleaveIO1
@[Bool]
((\ (eta_aSp [OS=OneShot] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
GHC.Prim.catch# @GHC.Types.LiftedRep @GHC.Types.Lifted @[Bool]
@GHC.Exception.Type.SomeException
(\ (eta1_aUj [OS=OneShot] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
GHC.Prim.readMVar#
@GHC.Types.Lifted @GHC.Prim.RealWorld @[Bool] ipv1_aUl eta1_aUj)
(System.IO.fixIO2 @[Bool])
eta_aSp)
`cast` (Sym (GHC.Types.N:IO[0] <[Bool]>_R)
:: (GHC.Prim.State# GHC.Prim.RealWorld
-> (# GHC.Prim.State# GHC.Prim.RealWorld, [Bool] #))
~R# IO [Bool]))
ipv_aUk
of
{ (# ipv2_aUp, ipv3_aUq #) ->
let {
q_s1HX [InlPrag=NOINLINE, Dmd=SL] :: [Bool]
q_s1HX = noinline @(forall a. a -> a) id @[Bool] ipv3_aUq } in
case GHC.Prim.putMVar# @GHC.Types.Lifted @GHC.Prim.RealWorld @[Bool]
ipv1_aUl
(GHC.Types.: @Bool GHC.Types.True q_s1HX)
ipv2_aUp
of s2#_aUw
{ __DEFAULT ->
case q_s1HX of {
[] ->
GHC.IO.Handle.Text.hPutStr2
GHC.IO.Handle.FD.stdout
GHC.Show.$fShowBool5
GHC.Types.True
s2#_aUw;
: v_aJ6 ds2_dRE ->
case v_aJ6 of vx_aV1 { __DEFAULT ->
GHC.IO.Handle.Text.hPutStr2
GHC.IO.Handle.FD.stdout
(case vx_aV1 of {
False -> GHC.Show.$fShowBool5;
True -> GHC.Show.$fShowBool4
})
GHC.Types.True
s2#_aUw
} } } } }
```
* The MVar stuff is to "tie the knot" in `fixIO`.
* The `putMVar#` fills in the MVar
* The `unsafeDupableInterleaveIO1` call returns a thunk in `ipv3` which reads the MVar when forced.
Now, **alas** the strictness analyser works out that `q` i used strictly in the continuation, so
the binding for `q` is done with a `case` not a `let` (look at CorePrep output). So we force `ipv3` too
early, before the `putMVar#` has run.
## Cure
The code for `fixIO` is this:
```
fixIO :: (a -> IO a) -> IO a
fixIO k = do
m <- newEmptyMVar
ans <- unsafeDupableInterleaveIO
(readMVar m `catch` \BlockedIndefinitelyOnMVar ->
throwIO FixIOException)
result <- k ans
putMVar m result
return result
```
Why all this MVar stuff? See `Note [fixST]` in `base:Control.Monad.ST.Imp`.
(And [this comment](https://github.com/haskell/core-libraries-committee/issues/242#issuecomment-1902317732).)
Looking at this code it's clear that we must do the `putMVar` before evaluating `result`.
But if we inline `fixIO`, the consumer will consume the `return result`, and the consumer
may well be strict in `result`. Something like
```
do { result <- fixIO m
; f result } -- where f is strict
```
Two simple solutions
* Do not inline `fixIO`.
* Wrap the result of `fixIO` in `lazy`, thus
```
putMVar m restult
return (lazy result)
```
I am not sure which is best. But the `lazy` solution is the one adopted by `Note [unsafePerformIO and strictness]` in GHC.IO.Unsafe, for a very very similar problem.https://gitlab.haskell.org/ghc/ghc/-/issues/24345Concurrent GHC sessions clobber RTS linker state2024-02-02T17:24:01ZAlexis KingConcurrent GHC sessions clobber RTS linker stateThe following example program (derived from `T3372`) illustrates how concurrent GHC sessions can clobber each other’s RTS linker state:
```haskell
{-# LANGUAGE MagicHash #-}
module Main where
import Data.Foldable
import System.Environm...The following example program (derived from `T3372`) illustrates how concurrent GHC sessions can clobber each other’s RTS linker state:
```haskell
{-# LANGUAGE MagicHash #-}
module Main where
import Data.Foldable
import System.Environment
import GHC (Ghc)
import qualified GHC
import qualified GHC.Driver.Monad as GHC
import qualified GHC.Driver.Session as GHC
import qualified GHC.Platform.Ways as GHC
import qualified GHC.Exts
main :: IO ()
main = do
let test1 = "M1.hs"
let test2 = "M2.hs"
writeFile test1 "module M where x = 1"
writeFile test2 "module M where x = 2"
ghc_1 <- newGhcSession
ghc_2 <- newGhcSession
line "1" $ runInSession ghc_1 $ load (test1, "M")
line "2" $ runInSession ghc_2 $ load (test2, "M")
line "3" $ runInSession ghc_1 $ eval "x"
line "4" $ runInSession ghc_2 $ eval "x"
line "5" $ runInSession ghc_1 $ eval "x"
where
line n a = putStr (n ++ ": ") >> a
type ModuleName = String
newGhcSession :: IO GHC.Session
newGhcSession = do
(libdir:_) <- getArgs
session <- GHC.runGhc (Just libdir) (GHC.reifyGhc pure)
runInSession session $ do
df <- GHC.getSessionDynFlags
let platform = GHC.targetPlatform df
GHC.setSessionDynFlags $
foldl' (flip GHC.setGeneralFlag')
df{GHC.ghcMode = GHC.CompManager,
GHC.ghcLink = GHC.LinkInMemory,
GHC.targetWays_ = GHC.hostFullWays,
GHC.verbosity = 0}
(concatMap (GHC.wayGeneralFlags platform) GHC.hostFullWays)
pure session
runInSession :: GHC.Session -> Ghc a -> IO a
runInSession = flip GHC.reflectGhc
load :: (FilePath, ModuleName) -> Ghc ()
load (f, mn) = do
target <- GHC.guessTarget f Nothing Nothing
GHC.setTargets [target]
res <- GHC.load GHC.LoadAllTargets
GHC.liftIO $ putStrLn ("Load " ++ showSuccessFlag res)
GHC.setContext
[ GHC.IIDecl $ GHC.simpleImportDecl $ GHC.mkModuleName "Prelude"
, GHC.IIDecl $ GHC.simpleImportDecl $ GHC.mkModuleName mn ]
where
showSuccessFlag GHC.Succeeded = "succeeded"
showSuccessFlag GHC.Failed = "failed"
eval :: String -> Ghc ()
eval e = do
show_e <- GHC.compileExpr $ "(show ("++ e ++")) :: String"
GHC.liftIO $ putStrLn (GHC.Exts.unsafeCoerce# show_e)
```
Compiling and running this program yields the following output:
```haskell
$ ghc -dynamic -package ghc Main
[1 of 2] Compiling Main ( Main.hs, Main.o )
[2 of 2] Linking Main [Objects changed]
$ ./Main "$(ghc --print-libdir)"
1: Load succeeded
2: Load succeeded
3: 1
4: 2
5: 2
```
The final line of output is the bug; it should print `1`, but instead it prints `2`.
## Explanation
The above program creates two concurrent GHC sessions and loads a module into each one. The modules share the same name and export the same symbol, but the symbol has a different value in each module.
Since the backend is the NCG and the program is dynamically-linked, each GHC session compiles the loaded module to a `.o` file, then links the `.o` file into a `.so` file on demand. The first time the two sessions each evaluate a reference to the loaded symbol, the results are `1` and `2`, respectively, which is correct. However, when the symbol is evaluated in the first session a second time, the `2` value from the second session is printed, instead. In effect, the module loaded in the second session has overwritten the module loaded in the first session, which is quite surprising.
This symbol clobbering only affects symbols loaded from shared objects, and it only affects the symbols resolved from an interpreted context. Even if symbols are “overwritten” in this way, references from native code will still refer to the original, correct symbols. However, any references in newly-created bytecode will be incorrect, and references from existing bytecode objects will become incorrect if the bytecode objects are relinked. Note also that the clobbering is truly on a symbol-by-symbol basis: if the second module were to export some, but not all, of the first module’s symbols, the values of those symbols would be selectively clobbered, leaving the first session in an inconsistent state. If the replaced symbols’ types differ from their replacements, memory corruption or segfaults are likely to occur.
## Cause
Currently, the state of the RTS linker is process-global. This includes the RTS symbol table, the list of loaded code objects, and the list of loaded shared libraries. GHC generally expects that it is the exclusive client of the RTS linker, and this assumption is essentially always correct, but concurrent GHC sessions break this assumption, which can result in various misbehaviors.
When the RTS linker is used to load static `.o` files or `.a` archives, it does all the work of loading the objects itself, including maintaining its own symbol table. For this reason, if the above example is compiled without the `-dynamic` option, the outcome is quite different:
```
$ ghc -package ghc Main
[1 of 2] Compiling Main ( Main.hs, Main.o )
[2 of 2] Linking Main [Objects changed]
$ ./Main "$(ghc --print-libdir)"
1: Load succeeded
2: Load succeeded
3: 1
GHC runtime linker: fatal error: I found a duplicate definition for symbol
M_x_closure
whilst processing object file
/tmp/haskell/M2.o
The symbol was previously defined in
/tmp/haskell/M1.o
This could be caused by:
* Loading two different object files which export the same symbol
* Specifying the same object file twice on the GHCi command line
* An incorrect `package.conf' entry, causing some object to be
loaded twice.
4: Main: loadObj "/tmp/haskell/M2.o": failed
```
Since the RTS maintains its own symbol table in this configuration, it can detect the symbol collision and report the error. This behavior is arguably still wrong—there is no fundamental reason that the two sessions must share a symbol table, and other session state is kept separate—but it’s at least less mysterious.
In contrast, when the RTS loads a shared library, it defers the work to the system dynamic linker (via `dlopen` on Linux and macOS). System dynamic linkers do not provide APIs to obtain the full list of symbols provided by a shared library, so the RTS cannot possibly determine whether two libraries provide conflicting symbols. When a symbol is looked up, each library is tried in order until the symbol is found, starting from the library that was most recently loaded. Within a single interpreter session, this strategy provides the generally-desirable property that new symbols shadow old ones (since most symbol conflicts arise from loading a new version of the same code), but it is much less defensible if multiple sessions exist in the same process.https://gitlab.haskell.org/ghc/ghc/-/issues/24189Async exceptions ignored and reraised by `catch` still update enclosing thunks2023-11-14T15:36:53ZAlexis KingAsync exceptions ignored and reraised by `catch` still update enclosing thunksThe following program produces a surprising result:
```haskell
import Control.Concurrent
import Control.Exception
import System.IO.Unsafe
main :: IO ()
main = do
let io_thunk = unsafePerformIO $
catch (threadDelay 1000000 *> ...The following program produces a surprising result:
```haskell
import Control.Concurrent
import Control.Exception
import System.IO.Unsafe
main :: IO ()
main = do
let io_thunk = unsafePerformIO $
catch (threadDelay 1000000 *> pure True)
(\(_ :: ErrorCall) -> pure False)
eval_thread <- forkIO (evaluate io_thunk *> pure ())
threadDelay 500000
killThread eval_thread
print io_thunk
```
Intuitively, the use of `catch` should not affect the meaning of this program, as it only catches `ErrorCall` exceptions, which will never be raised. However, its presence *does* affect the program’s result, and quite substantially: it causes the program to terminate with an exception.
```
$ ghc A.hs
[1 of 2] Compiling Main ( A.hs, A.o )
[2 of 2] Linking A
$ ./A
A: thread killed
```
This is quite surprising, as the same program with the `catch` removed simply prints `True` and exits successfully.[^1]
## The cause of the bug
This behavior is an unfortunate consequence of the way `catch` and asynchronous exceptions interact. When an async exception is delivered to a thread, the RTS takes care to suspend the work of any thunks currently being evaluated as it unwinds the stack. However, as soon as the RTS encounters a `CATCH_FRAME`, the exception is effectively converted to a synchronous exception. From the perspective of the RTS, all such frames are interchangeable, as the `catch#` primop knows nothing about the `Exception` class and therefore always catches all exceptions; `catch` is implemented by simply examining the caught exception and reraising it if it is not of the desired type.
This implementation strategy for `catch` fundamentally prevents `io_thunk` from the above program from being suspended upon delivery of an async exception. When the RTS encounters the `CATCH_FRAME`, it must resume execution from that point, so the unwound portion of the current evaluation context can only be discarded.
## Workarounds
Technically, this bug can be avoided by simply uninterruptibly masking asynchronous exceptions whenever `catch` is used within `unsafePerformIO`. However, this is a very heavy hammer, and it is not particularly satisfying.
Given sufficiently deep knowledge of both the RTS and the implementation of `catch`, it is technically possible to implement a more complete workaround. The following variant of the above program behaves as desired:
```haskell
import Control.Concurrent
import Control.Exception
import System.IO.Unsafe
main :: IO ()
main = do
let io_thunk = unsafePerformIO $ mask $ \restore -> do
let body = unsafePerformIO $ threadDelay 1000000 *> pure True
go = restore $ catches (evaluate body)
[ Handler $ \(_ :: ErrorCall) -> pure False
, Handler $ \(exn :: SomeAsyncException) -> do
myself <- myThreadId
throwTo myself exn
go
]
go
eval_thread <- forkIO (evaluate io_thunk *> pure ())
threadDelay 500000
killThread eval_thread
print io_thunk
```
The details are remarkably subtle:
* The body of the `catch` must be split into a separate thunk to give the RTS a place to store the suspended evaluation context.
* When an async exception is raised, it must be explicitly caught and reraised using `throwTo`, which effectively suspends the outer thunk. When that thunk is resumed, it must continue by resuming evaluation of the inner thunk.
* `mask` must be used not because async exceptions must be masked, but because they must be *unmasked*: `catch` implicitly masks exceptions within each handler, so if exceptions were not explicitly unmasked, they would incorrectly remain masked during the recursive call to `go`.
In theory, this is not a perfect solution: catching `SomeAsyncException` is not actually guaranteed to catch all async exceptions. However, it is likely good enough in practice. Still, it is quite complex, and I expect few Haskell programmers would fully understand how it works or why/when it is needed. At the very least, the `Control.Exception` documentation ought to be clarified to warn about this pitfall.
[^1]: As of this writing, this difference can only be observed if the program is compiled. If evaluated in GHCi, the program *always* terminates with an exception whether the `catch` is present or not, which I’ve reported separately as #24187.https://gitlab.haskell.org/ghc/ghc/-/issues/24187Thunk is incorrectly updated on async exception in GHCi2023-11-14T15:38:34ZAlexis KingThunk is incorrectly updated on async exception in GHCiThe following program produces different results depending on whether it is compiled or interpreted:
```haskell
import Control.Concurrent
import Control.Exception
import System.IO.Unsafe
main :: IO ()
main = do
let io_thunk = unsafeP...The following program produces different results depending on whether it is compiled or interpreted:
```haskell
import Control.Concurrent
import Control.Exception
import System.IO.Unsafe
main :: IO ()
main = do
let io_thunk = unsafePerformIO $ threadDelay 1000000 *> pure ()
eval_thread <- forkIO (evaluate io_thunk *> pure ())
threadDelay 500000
killThread eval_thread
print io_thunk
```
When compiled, the program prints `()`, but when interpreted, it terminates with an exception:
```
$ ghc A.hs
[1 of 2] Compiling Main ( A.hs, A.o )
[2 of 2] Linking A
$ ./A
()
$ ghc --run A.hs
<interactive>: thread killed
```
It seems like `io_thunk` is somehow being incorrectly overwritten as if the async exception were in fact raised synchronously.https://gitlab.haskell.org/ghc/ghc/-/issues/24140Eventlog + Debugging flag order shows unexpected behaviour2023-11-22T14:27:43ZJavier SagredoEventlog + Debugging flag order shows unexpected behaviour## Summary
Order of debug options and eventlog result in unexpected behavior.
- `-Dm`: emits messages `-Dm` to stderr :white_check_mark:
- `-Dm -l`: emits messages `-Dm` to the eventlog :white_check_mark:
- `-l -Dm`: emits `-Dm` to s...## Summary
Order of debug options and eventlog result in unexpected behavior.
- `-Dm`: emits messages `-Dm` to stderr :white_check_mark:
- `-Dm -l`: emits messages `-Dm` to the eventlog :white_check_mark:
- `-l -Dm`: emits `-Dm` to stderr and **enables what I believe is `-Ds`** :warning:
## Steps to reproduce
Any program would suffice. I can provide this one:
```bash
$ cat A.hs
module Main where
{-# NOINLINE fib #-}
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
main = do
print (fib 30)
$ ghc A.hs -debug
[1 of 2] Compiling Main ( A.hs, A.o )
[2 of 2] Linking A [Objects changed]
$ ./A +RTS -Dm 2>&1 | head
STM: 0x5b0780 : lock_stm()
STM: stmPreGCHook
STM: 0x5b0780 : unlock_stm()
STM: 0x5b0780 : lock_stm()
STM: stmPreGCHook
STM: 0x5b0780 : unlock_stm()
STM: 0x5b0780 : lock_stm()
STM: stmPreGCHook
STM: 0x5b0780 : unlock_stm()
STM: 0x5b0780 : lock_stm()
$ ./A +RTS -l -Dm 2>&1 | head
created capset 0 of type 2
created capset 1 of type 3
cap 0: initialised
assigned cap 0 to capset 0
assigned cap 0 to capset 1
cap 0: created thread 1[""]
cap 0: running thread 1[""] (ThreadRunGHC)
cap 0: thread 1[""] stopped (stack overflow, size 104)
cap 0: running thread 1[""] (ThreadRunGHC)
cap 0: thread 1[""] stopped (yielding)
$ ./A +RTS -Dm -l 2>&1 | head
832040
```
## Expected behavior
I would expect `-Ds` not to be enabled by this combination of flags, and furthermore I though the order of the options should not modify the behaviour.
## Environment
* GHC version used: 9.6.3
Optional:
* Operating System: Ubuntu 23.10
* System Architecture: x86_64-linuxMatthew PickeringMatthew Pickeringhttps://gitlab.haskell.org/ghc/ghc/-/issues/23866Possibly a variant of "Specialiser creates bogus specialisations" (#23109) th...2023-09-01T13:08:35ZMikolaj KonarskiPossibly a variant of "Specialiser creates bogus specialisations" (#23109) that affects 9.4.6This looks like a repeat of #23109 but for GHC 9.4.6. GHC 9.2.8 is not affected (from the looks of it, GHC 9.2.8 doesn't manage to specialize as much in this case). `-O0` is not affected.
The following commit breaks things by making spe...This looks like a repeat of #23109 but for GHC 9.4.6. GHC 9.2.8 is not affected (from the looks of it, GHC 9.2.8 doesn't manage to specialize as much in this case). `-O0` is not affected.
The following commit breaks things by making specialization fire more often (as verified with https://hackage.haskell.org/package/inspection-testing)
https://github.com/Mikolaj/horde-ad/commit/85d7ecc5aaaf3775476ca90c049984b444a359d6
The commit doesn't do anything except tweaking `SPECIALIZE` pragmas. The unsoundess manifests similarly as in #23109, e.g., with
```
~/r/horde-ad$ cabal test simplifiedOnlyTest --test-options='-p 3concatBuild22' --enable-optimization -ftest_seq --ghc-options='-dcore-lint -dstg-lint -dcmm-lint -dtag-inference-checks'
Build profile: -w ghc-9.4.6 -O1
In order, the following will be built (use -v for more details):
- horde-ad-0.1.0.0 (test:simplifiedOnlyTest) (first run)
Preprocessing test suite 'simplifiedOnlyTest' for horde-ad-0.1.0.0..
Building test suite 'simplifiedOnlyTest' for horde-ad-0.1.0.0..
Running 1 test suites...
Test suite simplifiedOnlyTest: RUNNING...
Tests for simplified horde-ad
Short_tests
3concatBuild22: FAIL
Exception: fromVector: rank mismatch (9,0)
CallStack (from HasCallStack):
error, called at ./Data/Array/Internal/RankedG.hs:152:45 in orthotope-0.1.6.0-d5eafb3329d4001e31e883540011090b1202463d41446e08b8b1a45899a51b89:Data.Array.Internal.RankedG
fromVector, called at ./Data/Array/Internal/RankedS.hs:133:21 in orthotope-0.1.6.0-d5eafb3329d4001e31e883540011090b1202463d41446e08b8b1a45899a51b89:Data.Array.Internal.RankedS
```
The following commit reverts a half of the commit above and makes the problem go away.
https://github.com/Mikolaj/horde-ad/commit/eed4f96ddd72e8c97f958196721b42c9fba9e68c
I haven't made any more minimization. I haven't tested with the typing plugins versions that are not yet on Hackage. We don't have `-fno-polymorphic-specialisation` in GHC 9.4, so I couldn't test if it prevents the problem.
CC: @christiaanbZubinZubinhttps://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/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/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 Gamari