GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2024-02-27T15:21:12Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/24464Core lint error when compiling distributed-closure-0.5.0.0 with 9.8.12024-02-27T15:21:12ZZubinCore lint error when compiling distributed-closure-0.5.0.0 with 9.8.1```bash
$ cabal get distributed-closure-0.5.0.0
$ cd distributed-closure-0.5.0.0
$ cat > cabal.project << EOF
packages: .
package distributed-closure
ghc-options: -dlint
EOF
$ cabal build -w ghc-9.8.1
```
```
[ 5 of 10] Compiling Cont...```bash
$ cabal get distributed-closure-0.5.0.0
$ cd distributed-closure-0.5.0.0
$ cat > cabal.project << EOF
packages: .
package distributed-closure
ghc-options: -dlint
EOF
$ cabal build -w ghc-9.8.1
```
```
[ 5 of 10] Compiling Control.Comonad.Static ( src/Control/Comonad/Static.hs, dist/build/Control/Comonad/Static.o, dist/build/Control/Comonad/Static.dyn_o )
*** Core Lint errors : in result of Float out(FOS {Lam = Just 0,
Consts = True,
OverSatApps = False}) ***
src/Control/Comonad/Static.hs:18:22: warning:
Found makeStatic nested in an expression
In the RHS of $dmstaticDuplicate :: forall (w :: * -> *) a.
(StaticExtend w, Typeable a) =>
w a -> w (w a)
In the unfolding of $dmstaticDuplicate :: forall (w :: * -> *) a.
(StaticExtend w, Typeable a) =>
w a -> w (w a)
In the body of lambda with binder w_adSi :: * -> *
In the body of lambda with binder $dStaticExtend_adUy :: StaticExtend
w_adSi
In the body of lambda with binder a_adUB :: *
In the body of lambda with binder eta_B0 :: Typeable a_adUB
In an occurrence of makeStatic :: forall a.
(Int, Int) -> a -> StaticPtr a
Substitution: <InScope = {w_adSi a_adUB}
IdSubst = []
TvSubst = [adSi :-> w_adSi, adUB :-> a_adUB]
CvSubst = []>
```9.12.1https://gitlab.haskell.org/ghc/ghc/-/issues/21692Static pointers + defer-type-errors Lint error2022-09-01T15:55:11ZKrzysztof GogolewskiStatic pointers + defer-type-errors Lint errorThis program fails Lint with `-fdefer-type-errors`:
```haskell
{-# LANGUAGE StaticPointers #-}
module T where
import GHC.StaticPtr
p :: StaticPtr Bool
p = static 'a'
```
```
*** Core Lint errors : in result of Desugar (before optimiz...This program fails Lint with `-fdefer-type-errors`:
```haskell
{-# LANGUAGE StaticPointers #-}
module T where
import GHC.StaticPtr
p :: StaticPtr Bool
p = static 'a'
```
```
*** Core Lint errors : in result of Desugar (before optimization) ***
<no location info>: warning:
Non-CoVar has coercion type co_avC :: Char ~# Bool
Substitution: [TCvSubst
In scope: InScope {}
Type env: []
Co env: []]
*** Offending Program ***
Rec {
co_avC :: Char ~# Bool
[LclId[CoVarId]]
co_avC
= case typeError ...
```
If Lint is disabled, it fails with an assertion failure or a panic.
Related: #18467https://gitlab.haskell.org/ghc/ghc/-/issues/20731Static pointers should work with RebindableSyntax2021-11-24T11:37:26ZKrzysztof GogolewskiStatic pointers should work with RebindableSyntaxWhen `RebindableSyntax` is enabled, GHC uses functions such as `fromInteger`, `fromString` from scope, rather than relying on the typeclasses. This principle is not upheld for `fromStaticPtr`:
```haskell
{-# LANGUAGE RebindableSyntax, S...When `RebindableSyntax` is enabled, GHC uses functions such as `fromInteger`, `fromString` from scope, rather than relying on the typeclasses. This principle is not upheld for `fromStaticPtr`:
```haskell
{-# LANGUAGE RebindableSyntax, StaticPointers, NoMonomorphismRestriction #-}
module M where
data T = MkT
fromInteger :: a -> T
fromInteger _ = MkT
fromStaticPtr :: a -> T
fromStaticPtr _ = MkT
x = 0
y = static 'a'
```
gives
```
x :: T
y :: GHC.StaticPtr.IsStatic t => t GHC.Types.Char
```
The type of `y` should be `T`, just like `x`.https://gitlab.haskell.org/ghc/ghc/-/issues/20727Static pointers should work with monomorphism restriction2022-08-22T14:26:47ZKrzysztof GogolewskiStatic pointers should work with monomorphism restrictionThe `OverloadedStrings` extension lets users write
```
x = "a"
```
even if the monomorphism restriction is on - the constraint `IsString t` is defaulted to `String`.
`StaticPointers` are overloaded, they should also get this defaultin...The `OverloadedStrings` extension lets users write
```
x = "a"
```
even if the monomorphism restriction is on - the constraint `IsString t` is defaulted to `String`.
`StaticPointers` are overloaded, they should also get this defaulting. Currently, the code
```
y = static 'a'
```
is rejected when MR is on: GHC refuses to solve `IsStatic t`. It should default to `StaticPtr`.https://gitlab.haskell.org/ghc/ghc/-/issues/17507Static Pointers with type classes generate misleading errors2021-11-21T19:11:37ZSimon BealStatic Pointers with type classes generate misleading errors## Summary
Using static with members of type classes generates unexpected and misleading type errors. The type errors produced by GHC complain about unsatisfiable type class constraints when the problem is (implicitly) that there is an ...## Summary
Using static with members of type classes generates unexpected and misleading type errors. The type errors produced by GHC complain about unsatisfiable type class constraints when the problem is (implicitly) that there is an unclosed argument.
## Steps to reproduce
```
{-# LANGUAGE StaticPointers #-}
{-# LANGUAGE GADTs #-}
import Type.Reflection (TypeRep, Typeable, typeRep)
import GHC.StaticPtr
data Dynamic where
Typed :: a -> StaticPtr (TypeRep a) -> Dynamic
toDyn :: (Typeable a) => a -> Dynamic
toDyn x = Typed x (static typeRep)
main :: IO ()
main = undefined
```
Compiling this produces the following error:
```
• No instance for (Typeable a) arising from a use of ‘typeRep’
• In the body of a static form: typeRep
In the second argument of ‘Typed’, namely ‘(static typeRep)’
In the expression: Typed x (static typeRep)
|
11 | toDyn x = Typed x (static typeRep)
| ^^^^^^^
```
The type error is unexpected, given that the assumption that `static` can be thought of as `static :: a -> StaticPtr a`. We can then introduce a function
```
static2 :: a -> StaticPtr a
static2 = undefined
```
and replacing `static` with `static2` allows the code to typecheck.
## Expected behavior
When using `static` with the member of a type class, this should be treated the same, or similar to using `static` with an unclosed argument.
## Environment
* GHC version used: 8.6.5
Optional:
* Operating System: Windows 10
* System Architecture: 64 bithttps://gitlab.haskell.org/ghc/ghc/-/issues/16981StaticPtr related linker error2021-08-10T16:31:58ZDavid Simmons-DuffinStaticPtr related linker error## Summary
I often encounter a difficult-to-localize linker error that occurs in conjunction with the use of StaticPointers. The error can go away or reappear after simple code transformations that should be trivial, like for example re...## Summary
I often encounter a difficult-to-localize linker error that occurs in conjunction with the use of StaticPointers. The error can go away or reappear after simple code transformations that should be trivial, like for example replacing `\(_,_) -> undefined` with `\_ -> undefined`.
## Steps to reproduce
This git repository:
https://github.com/davidsd/static-ptr-linker-error
contains the smallest program I could make that exhibits the error. Tiny changes that shouldn't matter can make the error go away, and for this reason I have been unable to shrink the code further. You should be able to clone and run `stack build` to see the issue.
## Expected behavior
When I run `stack build`, I see the output
.stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/linker-error-exec/linker-error-exec-tmp/Main.o:ghc_5.c:function hs_spt_init_Main: error: undefined reference to 'rcs4_closure'
collect2: error: ld returned 1 exit status
`gcc' failed in phase `Linker'. (Exit code: 1)
-- While building custom Setup.hs for package static-ptr-linker-error-0.1.0.0 using:
/central/home/dssimmon/.stack/setup-exe-cache/x86_64-linux/Cabal-simple_mPHDZzAJ_2.0.1.0_ghc-8.2.2 --builddir=.stack-work/dist/x86_64-linux/Cabal-2.0.1.0 build exe:linker-error-exec --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
One can make the code compile by making small changes, for example
- redefining `closure = undefined`
- replacing `\(_,_) -> undefined` with `\_ -> undefined` in line 83
- replacing other subexpressions with `undefined`
- replacing `applyRemoteStatic k a = k 'bindRemoteStatic' a` with `applyRemoteStatic = bindRemoteStatic` in line 47 (those are supposed to be backticks, but I can't figure out how to escape them)
- turning on -O2
- many other ways
## Environment
* GHC version used: 8.2.2 (via lts-11.13, listed in stack.yaml)
I have also observed this issue with GHC 8.4.4 (lts-12.18).
Optional:
* Operating System:
* System Architecture:Matthew PickeringMatthew Pickeringhttps://gitlab.haskell.org/ghc/ghc/-/issues/16283StaticPointers pragma changes generated code even when the feature is not used2021-08-09T15:32:20ZÖmer Sinan AğacanStaticPointers pragma changes generated code even when the feature is not usedTried with GHC HEAD. Program:
```
module Main where
import Control.Concurrent
import System.Mem
nats :: [Int]
nats = [0 .. ]
main = do
let z = nats !! 400
print z
performGC
threadDelay 1000000
print (nats !! 900)
```
Compi...Tried with GHC HEAD. Program:
```
module Main where
import Control.Concurrent
import System.Mem
nats :: [Int]
nats = [0 .. ]
main = do
let z = nats !! 400
print z
performGC
threadDelay 1000000
print (nats !! 900)
```
Compile without any flags:
```
==================== Tidy Core ====================
2019-02-04 09:16:26.121849511 UTC
Result size of Tidy Core
= {terms: 45, types: 26, coercions: 0, joins: 0/0}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule1_r1zg :: GHC.Prim.Addr#
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule1_r1zg = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule2_r1zt :: GHC.Types.TrName
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule2_r1zt = GHC.Types.TrNameS $trModule1_r1zg
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule3_r1zu :: GHC.Prim.Addr#
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule3_r1zu = "Main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule4_r1zv :: GHC.Types.TrName
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule4_r1zv = GHC.Types.TrNameS $trModule3_r1zu
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Main.$trModule :: GHC.Types.Module
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
Main.$trModule = GHC.Types.Module $trModule2_r1zt $trModule4_r1zv
-- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}
nats :: [Int]
[GblId]
nats = enumFrom @ Int GHC.Enum.$fEnumInt (GHC.Types.I# 0#)
-- RHS size: {terms: 22, types: 13, coercions: 0, joins: 0/0}
main :: IO ()
[GblId]
main
= >>
@ IO
GHC.Base.$fMonadIO
@ ()
@ ()
(print
@ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 400#)))
(>>
@ IO
GHC.Base.$fMonadIO
@ ()
@ ()
performGC
(>>
@ IO
GHC.Base.$fMonadIO
@ ()
@ ()
(threadDelay (GHC.Types.I# 1000000#))
(print
@ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 900#)))))
-- RHS size: {terms: 2, types: 1, coercions: 0, joins: 0/0}
:Main.main :: IO ()
[GblId]
:Main.main = GHC.TopHandler.runMainIO @ () main
```
Compile with `-XStaticPointers`:
```
==================== Tidy Core ====================
2019-02-04 09:16:35.678350955 UTC
Result size of Tidy Core
= {terms: 67, types: 42, coercions: 0, joins: 0/0}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule1_r1zg :: GHC.Prim.Addr#
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule1_r1zg = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule2_r1zF :: GHC.Types.TrName
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule2_r1zF = GHC.Types.TrNameS $trModule1_r1zg
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule3_r1zG :: GHC.Prim.Addr#
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule3_r1zG = "Main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule4_r1zH :: GHC.Types.TrName
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
$trModule4_r1zH = GHC.Types.TrNameS $trModule3_r1zG
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Main.$trModule :: GHC.Types.Module
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
Main.$trModule = GHC.Types.Module $trModule2_r1zF $trModule4_r1zH
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
lvl_r1zI :: Int
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
lvl_r1zI = GHC.Types.I# 0#
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
nats :: [Int]
[GblId]
nats = enumFrom @ Int GHC.Enum.$fEnumInt lvl_r1zI
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
lvl1_r1zJ :: Int
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
lvl1_r1zJ = GHC.Types.I# 400#
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
lvl2_r1zK :: Int
[GblId]
lvl2_r1zK = !! @ Int nats lvl1_r1zJ
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
lvl3_r1zL :: IO ()
[GblId]
lvl3_r1zL = print @ Int GHC.Show.$fShowInt lvl2_r1zK
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
lvl4_r1zM :: Int
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
lvl4_r1zM = GHC.Types.I# 1000000#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
lvl5_r1zN :: IO ()
[GblId]
lvl5_r1zN = threadDelay lvl4_r1zM
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
lvl6_r1zO :: Int
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
lvl6_r1zO = GHC.Types.I# 900#
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
lvl7_r1zP :: Int
[GblId]
lvl7_r1zP = !! @ Int nats lvl6_r1zO
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
lvl8_r1zQ :: IO ()
[GblId]
lvl8_r1zQ = print @ Int GHC.Show.$fShowInt lvl7_r1zP
-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
lvl9_r1zR :: IO ()
[GblId]
lvl9_r1zR
= >> @ IO GHC.Base.$fMonadIO @ () @ () lvl5_r1zN lvl8_r1zQ
-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
lvl10_r1zS :: IO ()
[GblId]
lvl10_r1zS
= >> @ IO GHC.Base.$fMonadIO @ () @ () performGC lvl9_r1zR
-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
main :: IO ()
[GblId]
main = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl3_r1zL lvl10_r1zS
-- RHS size: {terms: 2, types: 1, coercions: 0, joins: 0/0}
:Main.main :: IO ()
[GblId]
:Main.main = GHC.TopHandler.runMainIO @ () main
```
Diff:
```
--- no_static_ptrs/GcStaticPointers.dump-simpl 2019-02-04 12:16:26.120066655 +0300
+++ static_ptrs/GcStaticPointers.dump-simpl 2019-02-04 12:16:35.675924328 +0300
@@ -1,9 +1,9 @@
==================== Tidy Core ====================
-2019-02-04 09:16:26.121849511 UTC
+2019-02-04 09:16:35.678350955 UTC
Result size of Tidy Core
- = {terms: 45, types: 26, coercions: 0, joins: 0/0}
+ = {terms: 67, types: 42, coercions: 0, joins: 0/0}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule1_r1zg :: GHC.Prim.Addr#
@@ -11,55 +11,91 @@
$trModule1_r1zg = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
-$trModule2_r1zt :: GHC.Types.TrName
+$trModule2_r1zF :: GHC.Types.TrName
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
-$trModule2_r1zt = GHC.Types.TrNameS $trModule1_r1zg
+$trModule2_r1zF = GHC.Types.TrNameS $trModule1_r1zg
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
-$trModule3_r1zu :: GHC.Prim.Addr#
+$trModule3_r1zG :: GHC.Prim.Addr#
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
-$trModule3_r1zu = "Main"#
+$trModule3_r1zG = "Main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
-$trModule4_r1zv :: GHC.Types.TrName
+$trModule4_r1zH :: GHC.Types.TrName
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
-$trModule4_r1zv = GHC.Types.TrNameS $trModule3_r1zu
+$trModule4_r1zH = GHC.Types.TrNameS $trModule3_r1zG
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Main.$trModule :: GHC.Types.Module
[GblId, Caf=NoCafRefs, Unf=OtherCon []]
-Main.$trModule = GHC.Types.Module $trModule2_r1zt $trModule4_r1zv
+Main.$trModule = GHC.Types.Module $trModule2_r1zF $trModule4_r1zH
--- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}
+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
+lvl_r1zI :: Int
+[GblId, Caf=NoCafRefs, Unf=OtherCon []]
+lvl_r1zI = GHC.Types.I# 0#
+
+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
nats :: [Int]
[GblId]
-nats = enumFrom @ Int GHC.Enum.$fEnumInt (GHC.Types.I# 0#)
+nats = enumFrom @ Int GHC.Enum.$fEnumInt lvl_r1zI
+
+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
+lvl1_r1zJ :: Int
+[GblId, Caf=NoCafRefs, Unf=OtherCon []]
+lvl1_r1zJ = GHC.Types.I# 400#
+
+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
+lvl2_r1zK :: Int
+[GblId]
+lvl2_r1zK = !! @ Int nats lvl1_r1zJ
+
+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
+lvl3_r1zL :: IO ()
+[GblId]
+lvl3_r1zL = print @ Int GHC.Show.$fShowInt lvl2_r1zK
+
+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
+lvl4_r1zM :: Int
+[GblId, Caf=NoCafRefs, Unf=OtherCon []]
+lvl4_r1zM = GHC.Types.I# 1000000#
+
+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
+lvl5_r1zN :: IO ()
+[GblId]
+lvl5_r1zN = threadDelay lvl4_r1zM
+
+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
+lvl6_r1zO :: Int
+[GblId, Caf=NoCafRefs, Unf=OtherCon []]
+lvl6_r1zO = GHC.Types.I# 900#
+
+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
+lvl7_r1zP :: Int
+[GblId]
+lvl7_r1zP = !! @ Int nats lvl6_r1zO
+
+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
+lvl8_r1zQ :: IO ()
+[GblId]
+lvl8_r1zQ = print @ Int GHC.Show.$fShowInt lvl7_r1zP
+
+-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
+lvl9_r1zR :: IO ()
+[GblId]
+lvl9_r1zR
+ = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl5_r1zN lvl8_r1zQ
+
+-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
+lvl10_r1zS :: IO ()
+[GblId]
+lvl10_r1zS
+ = >> @ IO GHC.Base.$fMonadIO @ () @ () performGC lvl9_r1zR
--- RHS size: {terms: 22, types: 13, coercions: 0, joins: 0/0}
+-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
main :: IO ()
[GblId]
-main
- = >>
- @ IO
- GHC.Base.$fMonadIO
- @ ()
- @ ()
- (print
- @ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 400#)))
- (>>
- @ IO
- GHC.Base.$fMonadIO
- @ ()
- @ ()
- performGC
- (>>
- @ IO
- GHC.Base.$fMonadIO
- @ ()
- @ ()
- (threadDelay (GHC.Types.I# 1000000#))
- (print
- @ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 900#)))))
+main = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl3_r1zL lvl10_r1zS
-- RHS size: {terms: 2, types: 1, coercions: 0, joins: 0/0}
:Main.main :: IO ()
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | |
| 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":"StaticPointers pragma changes generated code even when the feature is not used","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Tried with GHC HEAD. Program:\r\n\r\n{{{\r\nmodule Main where\r\n\r\nimport Control.Concurrent\r\nimport System.Mem\r\n\r\nnats :: [Int]\r\nnats = [0 .. ]\r\n\r\nmain = do\r\n let z = nats !! 400\r\n print z\r\n performGC\r\n threadDelay 1000000\r\n print (nats !! 900)\r\n}}}\r\n\r\nCompile without any flags:\r\n\r\n{{{\r\n==================== Tidy Core ====================\r\n2019-02-04 09:16:26.121849511 UTC\r\n\r\nResult size of Tidy Core\r\n = {terms: 45, types: 26, coercions: 0, joins: 0/0}\r\n\r\n-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}\r\n$trModule1_r1zg :: GHC.Prim.Addr#\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule1_r1zg = \"main\"#\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n$trModule2_r1zt :: GHC.Types.TrName\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule2_r1zt = GHC.Types.TrNameS $trModule1_r1zg\r\n\r\n-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}\r\n$trModule3_r1zu :: GHC.Prim.Addr#\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule3_r1zu = \"Main\"#\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n$trModule4_r1zv :: GHC.Types.TrName\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule4_r1zv = GHC.Types.TrNameS $trModule3_r1zu\r\n\r\n-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}\r\nMain.$trModule :: GHC.Types.Module\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\nMain.$trModule = GHC.Types.Module $trModule2_r1zt $trModule4_r1zv\r\n\r\n-- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}\r\nnats :: [Int]\r\n[GblId]\r\nnats = enumFrom @ Int GHC.Enum.$fEnumInt (GHC.Types.I# 0#)\r\n\r\n-- RHS size: {terms: 22, types: 13, coercions: 0, joins: 0/0}\r\nmain :: IO ()\r\n[GblId]\r\nmain\r\n = >>\r\n @ IO\r\n GHC.Base.$fMonadIO\r\n @ ()\r\n @ ()\r\n (print\r\n @ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 400#)))\r\n (>>\r\n @ IO\r\n GHC.Base.$fMonadIO\r\n @ ()\r\n @ ()\r\n performGC\r\n (>>\r\n @ IO\r\n GHC.Base.$fMonadIO\r\n @ ()\r\n @ ()\r\n (threadDelay (GHC.Types.I# 1000000#))\r\n (print\r\n @ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 900#)))))\r\n\r\n-- RHS size: {terms: 2, types: 1, coercions: 0, joins: 0/0}\r\n:Main.main :: IO ()\r\n[GblId]\r\n:Main.main = GHC.TopHandler.runMainIO @ () main\r\n}}}\r\n\r\nCompile with `-XStaticPointers`:\r\n\r\n{{{\r\n\r\n==================== Tidy Core ====================\r\n2019-02-04 09:16:35.678350955 UTC\r\n\r\nResult size of Tidy Core\r\n = {terms: 67, types: 42, coercions: 0, joins: 0/0}\r\n\r\n-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}\r\n$trModule1_r1zg :: GHC.Prim.Addr#\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule1_r1zg = \"main\"#\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n$trModule2_r1zF :: GHC.Types.TrName\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule2_r1zF = GHC.Types.TrNameS $trModule1_r1zg\r\n\r\n-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}\r\n$trModule3_r1zG :: GHC.Prim.Addr#\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule3_r1zG = \"Main\"#\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n$trModule4_r1zH :: GHC.Types.TrName\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n$trModule4_r1zH = GHC.Types.TrNameS $trModule3_r1zG\r\n\r\n-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}\r\nMain.$trModule :: GHC.Types.Module\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\nMain.$trModule = GHC.Types.Module $trModule2_r1zF $trModule4_r1zH\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\nlvl_r1zI :: Int\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\nlvl_r1zI = GHC.Types.I# 0#\r\n\r\n-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\nnats :: [Int]\r\n[GblId]\r\nnats = enumFrom @ Int GHC.Enum.$fEnumInt lvl_r1zI\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\nlvl1_r1zJ :: Int\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\nlvl1_r1zJ = GHC.Types.I# 400#\r\n\r\n-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\nlvl2_r1zK :: Int\r\n[GblId]\r\nlvl2_r1zK = !! @ Int nats lvl1_r1zJ\r\n\r\n-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\nlvl3_r1zL :: IO ()\r\n[GblId]\r\nlvl3_r1zL = print @ Int GHC.Show.$fShowInt lvl2_r1zK\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\nlvl4_r1zM :: Int\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\nlvl4_r1zM = GHC.Types.I# 1000000#\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\nlvl5_r1zN :: IO ()\r\n[GblId]\r\nlvl5_r1zN = threadDelay lvl4_r1zM\r\n\r\n-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\nlvl6_r1zO :: Int\r\n[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\nlvl6_r1zO = GHC.Types.I# 900#\r\n\r\n-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\nlvl7_r1zP :: Int\r\n[GblId]\r\nlvl7_r1zP = !! @ Int nats lvl6_r1zO\r\n\r\n-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\nlvl8_r1zQ :: IO ()\r\n[GblId]\r\nlvl8_r1zQ = print @ Int GHC.Show.$fShowInt lvl7_r1zP\r\n\r\n-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}\r\nlvl9_r1zR :: IO ()\r\n[GblId]\r\nlvl9_r1zR\r\n = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl5_r1zN lvl8_r1zQ\r\n\r\n-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}\r\nlvl10_r1zS :: IO ()\r\n[GblId]\r\nlvl10_r1zS\r\n = >> @ IO GHC.Base.$fMonadIO @ () @ () performGC lvl9_r1zR\r\n\r\n-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}\r\nmain :: IO ()\r\n[GblId]\r\nmain = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl3_r1zL lvl10_r1zS\r\n\r\n-- RHS size: {terms: 2, types: 1, coercions: 0, joins: 0/0}\r\n:Main.main :: IO ()\r\n[GblId]\r\n:Main.main = GHC.TopHandler.runMainIO @ () main\r\n}}}\r\n\r\nDiff:\r\n\r\n{{{\r\n--- no_static_ptrs/GcStaticPointers.dump-simpl\t2019-02-04 12:16:26.120066655 +0300\r\n+++ static_ptrs/GcStaticPointers.dump-simpl\t2019-02-04 12:16:35.675924328 +0300\r\n@@ -1,9 +1,9 @@\r\n \r\n ==================== Tidy Core ====================\r\n-2019-02-04 09:16:26.121849511 UTC\r\n+2019-02-04 09:16:35.678350955 UTC\r\n \r\n Result size of Tidy Core\r\n- = {terms: 45, types: 26, coercions: 0, joins: 0/0}\r\n+ = {terms: 67, types: 42, coercions: 0, joins: 0/0}\r\n \r\n -- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}\r\n $trModule1_r1zg :: GHC.Prim.Addr#\r\n@@ -11,55 +11,91 @@\r\n $trModule1_r1zg = \"main\"#\r\n \r\n -- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n-$trModule2_r1zt :: GHC.Types.TrName\r\n+$trModule2_r1zF :: GHC.Types.TrName\r\n [GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n-$trModule2_r1zt = GHC.Types.TrNameS $trModule1_r1zg\r\n+$trModule2_r1zF = GHC.Types.TrNameS $trModule1_r1zg\r\n \r\n -- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}\r\n-$trModule3_r1zu :: GHC.Prim.Addr#\r\n+$trModule3_r1zG :: GHC.Prim.Addr#\r\n [GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n-$trModule3_r1zu = \"Main\"#\r\n+$trModule3_r1zG = \"Main\"#\r\n \r\n -- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n-$trModule4_r1zv :: GHC.Types.TrName\r\n+$trModule4_r1zH :: GHC.Types.TrName\r\n [GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n-$trModule4_r1zv = GHC.Types.TrNameS $trModule3_r1zu\r\n+$trModule4_r1zH = GHC.Types.TrNameS $trModule3_r1zG\r\n \r\n -- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}\r\n Main.$trModule :: GHC.Types.Module\r\n [GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n-Main.$trModule = GHC.Types.Module $trModule2_r1zt $trModule4_r1zv\r\n+Main.$trModule = GHC.Types.Module $trModule2_r1zF $trModule4_r1zH\r\n \r\n--- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}\r\n+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n+lvl_r1zI :: Int\r\n+[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n+lvl_r1zI = GHC.Types.I# 0#\r\n+\r\n+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\n nats :: [Int]\r\n [GblId]\r\n-nats = enumFrom @ Int GHC.Enum.$fEnumInt (GHC.Types.I# 0#)\r\n+nats = enumFrom @ Int GHC.Enum.$fEnumInt lvl_r1zI\r\n+\r\n+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n+lvl1_r1zJ :: Int\r\n+[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n+lvl1_r1zJ = GHC.Types.I# 400#\r\n+\r\n+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\n+lvl2_r1zK :: Int\r\n+[GblId]\r\n+lvl2_r1zK = !! @ Int nats lvl1_r1zJ\r\n+\r\n+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\n+lvl3_r1zL :: IO ()\r\n+[GblId]\r\n+lvl3_r1zL = print @ Int GHC.Show.$fShowInt lvl2_r1zK\r\n+\r\n+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n+lvl4_r1zM :: Int\r\n+[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n+lvl4_r1zM = GHC.Types.I# 1000000#\r\n+\r\n+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n+lvl5_r1zN :: IO ()\r\n+[GblId]\r\n+lvl5_r1zN = threadDelay lvl4_r1zM\r\n+\r\n+-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}\r\n+lvl6_r1zO :: Int\r\n+[GblId, Caf=NoCafRefs, Unf=OtherCon []]\r\n+lvl6_r1zO = GHC.Types.I# 900#\r\n+\r\n+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\n+lvl7_r1zP :: Int\r\n+[GblId]\r\n+lvl7_r1zP = !! @ Int nats lvl6_r1zO\r\n+\r\n+-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}\r\n+lvl8_r1zQ :: IO ()\r\n+[GblId]\r\n+lvl8_r1zQ = print @ Int GHC.Show.$fShowInt lvl7_r1zP\r\n+\r\n+-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}\r\n+lvl9_r1zR :: IO ()\r\n+[GblId]\r\n+lvl9_r1zR\r\n+ = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl5_r1zN lvl8_r1zQ\r\n+\r\n+-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}\r\n+lvl10_r1zS :: IO ()\r\n+[GblId]\r\n+lvl10_r1zS\r\n+ = >> @ IO GHC.Base.$fMonadIO @ () @ () performGC lvl9_r1zR\r\n \r\n--- RHS size: {terms: 22, types: 13, coercions: 0, joins: 0/0}\r\n+-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}\r\n main :: IO ()\r\n [GblId]\r\n-main\r\n- = >>\r\n- @ IO\r\n- GHC.Base.$fMonadIO\r\n- @ ()\r\n- @ ()\r\n- (print\r\n- @ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 400#)))\r\n- (>>\r\n- @ IO\r\n- GHC.Base.$fMonadIO\r\n- @ ()\r\n- @ ()\r\n- performGC\r\n- (>>\r\n- @ IO\r\n- GHC.Base.$fMonadIO\r\n- @ ()\r\n- @ ()\r\n- (threadDelay (GHC.Types.I# 1000000#))\r\n- (print\r\n- @ Int GHC.Show.$fShowInt (!! @ Int nats (GHC.Types.I# 900#)))))\r\n+main = >> @ IO GHC.Base.$fMonadIO @ () @ () lvl3_r1zL lvl10_r1zS\r\n \r\n -- RHS size: {terms: 2, types: 1, coercions: 0, joins: 0/0}\r\n :Main.main :: IO ()\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/15395Make StaticPtr (more) robust to code changes and recompilation2022-02-24T10:55:58ZrichardwMake StaticPtr (more) robust to code changes and recompilationDiscussion migrated from [this](https://www.reddit.com/r/haskell/comments/8z86q6/stability_of_staticptr/) thread on Reddit.
The [documentation](http://hackage.haskell.org/package/base-4.11.1.0/docs/GHC-StaticPtr.html) for GHC.StaticPtr ...Discussion migrated from [this](https://www.reddit.com/r/haskell/comments/8z86q6/stability_of_staticptr/) thread on Reddit.
The [documentation](http://hackage.haskell.org/package/base-4.11.1.0/docs/GHC-StaticPtr.html) for GHC.StaticPtr states that "Only processes launched from the same program binary are guaranteed to use the same set of keys."
On the other hand, this [blog post](https://ghc.haskell.org/trac/ghc/blog/simonpj/StaticPointers) by Simon suggests that the only sensible implementation of StaticPtr would be as (essentially) a package/module/identifier name triple, which sounds right to me. In this case two StaticPtrs should share the same key as long as they are in the same package/module and assigned to the same identifier.
My use case is similar to the one described [here](http://neilmitchell.blogspot.com/2017/09/existential-serialisation.html), where StaticPtr is used for (de)serialization of an existential. As pointed out in the comments to that post, this approach could break upon any recompilation of the code base.
Having static pointers stable only across instances of the same binary also seems like it would be a potential problem for the applications like CloudHaskell that StaticPointers was developed for. Presumably this means means all nodes need to be based on the same architecture and updates need to happen on every node simultaneously.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 8.4.3 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | Edsko, Facundo |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Make StaticPtr (more) robust to code changes and recompilation","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"8.6.1","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.4.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":["Edsko","Facundo"],"type":"FeatureRequest","description":"Discussion migrated from [https://www.reddit.com/r/haskell/comments/8z86q6/stability_of_staticptr/ this] thread on Reddit.\r\n\r\nThe [http://hackage.haskell.org/package/base-4.11.1.0/docs/GHC-StaticPtr.html documentation] for GHC.StaticPtr states that \"Only processes launched from the same program binary are guaranteed to use the same set of keys.\"\r\n\r\nOn the other hand, this [https://ghc.haskell.org/trac/ghc/blog/simonpj/StaticPointers blog post] by Simon suggests that the only sensible implementation of StaticPtr would be as (essentially) a package/module/identifier name triple, which sounds right to me. In this case two StaticPtrs should share the same key as long as they are in the same package/module and assigned to the same identifier.\r\n\r\nMy use case is similar to the one described [http://neilmitchell.blogspot.com/2017/09/existential-serialisation.html here], where StaticPtr is used for (de)serialization of an existential. As pointed out in the comments to that post, this approach could break upon any recompilation of the code base. \r\n\r\nHaving static pointers stable only across instances of the same binary also seems like it would be a potential problem for the applications like CloudHaskell that StaticPointers was developed for. Presumably this means means all nodes need to be based on the same architecture and updates need to happen on every node simultaneously.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/14770Allow static pointer expressions to have static pointer free variables2019-07-07T18:15:42ZTheKing01Allow static pointer expressions to have static pointer free variablesStatic pointer expressions can not have free variables. For example:
```hs
addPointers :: StaticPtr Int -> StaticPtr Int -> StaticPtr Int
addPointers x y = static (deRefStaticPtr x + deRefStaticPtr y)
```
would be invalid, since `x` an...Static pointer expressions can not have free variables. For example:
```hs
addPointers :: StaticPtr Int -> StaticPtr Int -> StaticPtr Int
addPointers x y = static (deRefStaticPtr x + deRefStaticPtr y)
```
would be invalid, since `x` and `y` are free variables. My proposal would be to make this code sample valid. In particular, my proposal is to allow free variables in `static` expressions if the free variables of the type `StaticPtr a` for some type `a`.
Let's say that `m` `n` are Static pointers to integers. The serialized form would of `addPointers m n` would be:
A pointer to the code `deRefStaticPtr x + deRefStaticPtr y`
The serialized form of `m`, associated with `x`.
The serialized form of `n`, associated with `y`.
When this is sent over the wire and dereferenced, the other machine would
Find `m` and `n`'s pointers
Find the code `deRefStaticPtr x + deRefStaticPtr y`
Evaluate `deRefStaticPtr x + deRefStaticPtr y`, substituting `m`'s value for `x`, and `n`'s value for y.
The reason this feature would be useful is would let the user modify and combine static pointers.
You might ask "why not let the user create a GADT to do that (like in https://hackage.haskell.org/package/distributed-static-0.3.8/docs/Control-Distributed-Static.html\#t:Static)?" The reason is that then it won't fuse (with recreating every fusion rule for that GADT). For example, if `m` is `static 7` and `n` is `static 3`, then `addPointers m n` can fuse to `static 11`.
This is more important then it seems. For example, say you have `composePointers :: StaticPtr (a -> b) -> StaticPtr (b -> c) -> StaticPtr (a -> c)`, and you compose a bunch of pointers. With the GADTS, every `composePointers` would be its own node, but with GHC support, it would probably fuse.
In general, dealing with a GADT requires a lot of "glue code" that can be eliminated with GHC support. I'm not sure if this proposal makes a performance difference (it probably makes it more efficient), but it would make code more elegant, cutting out all the glue code.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 8.2.2 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Allow static pointer expressions to have static pointer free variables","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.2.2","keywords":["StaticPointers"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"FeatureRequest","description":"Static pointer expressions can not have free variables. For example:\r\n\r\n{{{#!hs\r\naddPointers :: StaticPtr Int -> StaticPtr Int -> StaticPtr Int\r\naddPointers x y = static (deRefStaticPtr x + deRefStaticPtr y)\r\n}}}\r\n\r\nwould be invalid, since `x` and `y` are free variables. My proposal would be to make this code sample valid. In particular, my proposal is to allow free variables in `static` expressions if the free variables of the type `StaticPtr a` for some type `a`.\r\n\r\nLet's say that `m` `n` are Static pointers to integers. The serialized form would of `addPointers m n` would be:\r\n\r\nA pointer to the code `deRefStaticPtr x + deRefStaticPtr y`\r\n\r\nThe serialized form of `m`, associated with `x`.\r\n\r\nThe serialized form of `n`, associated with `y`.\r\n\r\nWhen this is sent over the wire and dereferenced, the other machine would\r\n\r\nFind `m` and `n`'s pointers\r\n\r\nFind the code `deRefStaticPtr x + deRefStaticPtr y`\r\n\r\nEvaluate `deRefStaticPtr x + deRefStaticPtr y`, substituting `m`'s value for `x`, and `n`'s value for y.\r\n\r\nThe reason this feature would be useful is would let the user modify and combine static pointers.\r\n\r\nYou might ask \"why not let the user create a GADT to do that (like in https://hackage.haskell.org/package/distributed-static-0.3.8/docs/Control-Distributed-Static.html#t:Static)?\" The reason is that then it won't fuse (with recreating every fusion rule for that GADT). For example, if `m` is `static 7` and `n` is `static 3`, then `addPointers m n` can fuse to `static 11`.\r\n\r\nThis is more important then it seems. For example, say you have `composePointers :: StaticPtr (a -> b) -> StaticPtr (b -> c) -> StaticPtr (a -> c)`, and you compose a bunch of pointers. With the GADTS, every `composePointers` would be its own node, but with GHC support, it would probably fuse.\r\n\r\nIn general, dealing with a GADT requires a lot of \"glue code\" that can be eliminated with GHC support. I'm not sure if this proposal makes a performance difference (it probably makes it more efficient), but it would make code more elegant, cutting out all the glue code.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/13306Problems with type inference for static expressions2022-01-26T11:02:30Zedsko@edsko.netProblems with type inference for static expressionsI've been running into some difficulties with type inference for static expressions; I suspect not enough type information might be propagated down. Below are a number of tests, all of which compare type inference for `static` with type ...I've been running into some difficulties with type inference for static expressions; I suspect not enough type information might be propagated down. Below are a number of tests, all of which compare type inference for `static` with type inference for a function
```hs
fakeStatic :: Typeable a => a -> StaticPtr a
fakeStatic = undefined
```
Apart from syntactic checks, I'd expect `static <expr>` and `fakeStatic <expr>` to behave more or less the same, but they don't. Here are some examples:
```hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StaticPointers #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
module Main where
import Data.Proxy
import Data.Typeable
import GHC.StaticPtr
{-------------------------------------------------------------------------------
Setup
-------------------------------------------------------------------------------}
-- Some kind of non-injective type family
type family NonInj a where
NonInj Bool = ()
NonInj Char = ()
-- To compare against the real static
fakeStatic :: Typeable a => a -> StaticPtr a
fakeStatic = undefined
{-------------------------------------------------------------------------------
Test 1: identity function
-------------------------------------------------------------------------------}
f1 :: Proxy a -> NonInj a -> NonInj a
f1 Proxy = id
f2 :: forall a. Typeable (NonInj a) => Proxy a -> StaticPtr (NonInj a -> NonInj a)
f2 Proxy = fakeStatic id
-- Fails with:
--
-- Couldn't match type ‘a0’ with ‘NonInj a’
-- Expected type: NonInj a -> NonInj a
-- Actual type: a0 -> a0
-- The type variable ‘a0’ is ambiguous
-- f3 :: forall a. Typeable (NonInj a) => Proxy a -> StaticPtr (NonInj a -> NonInj a)
-- f3 Proxy = static id
-- Fails with the same error
-- f4 :: forall a. Typeable (NonInj a) => Proxy a -> StaticPtr (NonInj a -> NonInj a)
-- f4 Proxy = (static id) :: StaticPtr (NonInj a -> NonInj a)
{-------------------------------------------------------------------------------
Test 2: adding some kind of universe
-------------------------------------------------------------------------------}
data U :: * -> * where
UB :: U Bool
UC :: U Char
f5 :: U a -> NonInj a -> NonInj a
f5 _ = id
-- This works just fine
f6 :: (Typeable a, Typeable (NonInj a)) => StaticPtr (U a -> NonInj a -> NonInj a)
f6 = static f5
-- but if we introduce Typeable ..
f7 :: Typeable a => U a -> NonInj a -> NonInj a
f7 _ = id
-- .. fakeStatic still works
f8 :: (Typeable a, Typeable (NonInj a)) => StaticPtr (U a -> NonInj a -> NonInj a)
f8 = fakeStatic f7
-- .. but static leads to a weird error:
-- No instance for (Typeable a) arising from a use of ‘f7’
-- f9 :: (Typeable a, Typeable (NonInj a)) => StaticPtr (U a -> NonInj a -> NonInj a)
-- f9 = static f7
{-------------------------------------------------------------------------------
Test 3: GADT wrapping StaticPtr
-------------------------------------------------------------------------------}
data Static :: * -> * where
StaticPtr :: StaticPtr a -> Static a
StaticApp :: Static (a -> b) -> Static a -> Static b
-- Serializable types can be embedded into Static; here we just support U
StaticBase :: U a -> Static (U a)
-- this is fine
f10 :: forall a. (Typeable a, Typeable (NonInj a)) => U a -> Static (NonInj a -> NonInj a)
f10 x = StaticPtr (fakeStatic f5) `StaticApp` (StaticBase x)
-- but this fails with
-- Couldn't match type ‘NonInj a -> NonInj a’
-- with ‘NonInj a0 -> NonInj a0’
-- Expected type: U a -> NonInj a -> NonInj a
-- Actual type: U a0 -> NonInj a0 -> NonInj a0
-- f11 :: forall a. (Typeable a, Typeable (NonInj a)) => U a -> Static (NonInj a -> NonInj a)
-- f11 x = StaticPtr (static f5) `StaticApp` (StaticBase x)
-- although in this case we can work around it with a type annotation:
-- (note that for f4 above this workaround didn't work)
f12 :: forall a. (Typeable a, Typeable (NonInj a)) => U a -> Static (NonInj a -> NonInj a)
f12 x = StaticPtr (static f5 :: StaticPtr (U a -> NonInj a -> NonInj a)) `StaticApp` (StaticBase x)
{-------------------------------------------------------------------------------
End of tests
-------------------------------------------------------------------------------}
main :: IO ()
main = putStrLn "Hi"
```https://gitlab.haskell.org/ghc/ghc/-/issues/12875GHC fails to link all StaticPointers-defining modules of a library in an exec...2020-01-23T19:36:33ZBen GamariGHC fails to link all StaticPointers-defining modules of a library in an executableConsider that you have a package called `lib` which exposes these modules,
```hs
module ALib.Types where
data AThing = AThing String
deriving (Show)
{-# LANGUAGE StaticPointers #-}
module ALib.Things where
import GHC.Sta...Consider that you have a package called `lib` which exposes these modules,
```hs
module ALib.Types where
data AThing = AThing String
deriving (Show)
{-# LANGUAGE StaticPointers #-}
module ALib.Things where
import GHC.StaticPtr
import ALib.Types
thing1 :: StaticPtr AThing
thing1 = static (AThing "hello")
```
Now consider that you have a server which reads a `StaticKey` of `StaticPtr AThing` and shows it,
```hs
import ALib.Types
main :: IO ()
main = do
key <- readFingerprint <$> getContents :: IO StaticKey
Just thing <- unsafeLookupStaticPtr key :: IO (Maybe (StaticPtr AThing))
print $ deRefStaticPtr thing
```
Naturally this executable will link against `lib`. However, this executable as-written will fail if given the key of `ALib.Things.thing1`. Fixing this requires that the executable explicitly import and use a definition from `ALib.Things`.
The problem appears to be that the linker elides the `ALib.Things` object from the final executable unless it refers to a symbol. Note that things also work fine if the server executable is dynamically linked.