GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2023-08-11T11:04:21Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/15437Internal error when applying a scoped type variable inside a typed expression...2023-08-11T11:04:21ZdminuosoInternal error when applying a scoped type variable inside a typed expression quotation```hs
{-# LANGUAGE TemplateHaskell #-}
import TestMod
f :: Int
f = $$(foo)
main :: IO ()
main = main
```
```hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module TestMod wh...```hs
{-# LANGUAGE TemplateHaskell #-}
import TestMod
f :: Int
f = $$(foo)
main :: IO ()
main = main
```
```hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module TestMod where
import Language.Haskell.TH.Syntax (Q, TExp)
get :: forall a. Int
get = 1
foo :: forall a. Q (TExp Int)
foo = [|| get @a ||]
```
```
Test.hs:6:8: error:
• The exact Name ‘a’ is not in scope
Probable cause: you used a unique Template Haskell name (NameU),
perhaps via newName, but did not bind it
If that's it, then -ddump-splices might be useful
• In the result of the splice:
$foo
To see what the splice expanded to, use -ddump-splices
In the Template Haskell splice $$(foo)
In the expression: $$(foo)
|
6 | f = $$(foo)
| ^^^
Test.hs:6:8: error:
• GHC internal error: ‘a’ is not in scope during type checking, but it passed the renamer
tcl_env of environment: [r3Kl :-> Identifier[f::Int, TopLevelLet [] True],
r3PI :-> Identifier[main::IO (), TopLevelLet [r3PI :-> main] True]]
• In the type ‘a’
In the expression: get @a
In the result of the splice:
$foo
To see what the splice expanded to, use -ddump-splices
|
6 | f = $$(foo)
|
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.4.3 |
| 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":"Internal error when applying a scoped type variable inside a typed expression quotation","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":[""],"type":"Bug","description":"{{{#!hs\r\n{-# LANGUAGE TemplateHaskell #-}\r\n\r\nimport TestMod\r\n\r\nf :: Int\r\nf = $$(foo)\r\n\r\nmain :: IO ()\r\nmain = main\r\n}}}\r\n\r\n{{{#!hs\r\n{-# LANGUAGE ScopedTypeVariables #-}\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE TypeApplications #-}\r\n\r\nmodule TestMod where\r\n\r\nimport Language.Haskell.TH.Syntax (Q, TExp)\r\n\r\nget :: forall a. Int\r\nget = 1\r\n\r\nfoo :: forall a. Q (TExp Int)\r\nfoo = [|| get @a ||]\r\n}}}\r\n\r\n{{{\r\nTest.hs:6:8: error:\r\n • The exact Name ‘a’ is not in scope\r\n Probable cause: you used a unique Template Haskell name (NameU),\r\n perhaps via newName, but did not bind it\r\n If that's it, then -ddump-splices might be useful\r\n • In the result of the splice:\r\n $foo\r\n To see what the splice expanded to, use -ddump-splices\r\n In the Template Haskell splice $$(foo)\r\n In the expression: $$(foo)\r\n |\r\n6 | f = $$(foo)\r\n | ^^^\r\n\r\nTest.hs:6:8: error:\r\n • GHC internal error: ‘a’ is not in scope during type checking, but it passed the renamer\r\n tcl_env of environment: [r3Kl :-> Identifier[f::Int, TopLevelLet [] True],\r\n r3PI :-> Identifier[main::IO (), TopLevelLet [r3PI :-> main] True]]\r\n • In the type ‘a’\r\n In the expression: get @a\r\n In the result of the splice:\r\n $foo\r\n To see what the splice expanded to, use -ddump-splices\r\n |\r\n6 | f = $$(foo)\r\n |\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->8.10.2https://gitlab.haskell.org/ghc/ghc/-/issues/21910Scoped type variables in typed quotes lose scope2022-07-25T09:20:36ZAndrasKovacsScoped type variables in typed quotes lose scopeConsider the following code. I used GHC-9.2.3.
```haskell
{-# language TemplateHaskell, ScopedTypeVariables #-}
import Language.Haskell.TH
f :: forall a. CodeQ a -> CodeQ a
f x = [|| let y :: a; y = $$x in y ||]
```
This typechecks, bu...Consider the following code. I used GHC-9.2.3.
```haskell
{-# language TemplateHaskell, ScopedTypeVariables #-}
import Language.Haskell.TH
f :: forall a. CodeQ a -> CodeQ a
f x = [|| let y :: a; y = $$x in y ||]
```
This typechecks, but the following doesn't:
```haskell
g :: Bool -> Bool
g x = $$(f [||x||])
```
I get
```
• Couldn't match expected type ‘a’ with actual type ‘Bool’
‘a’ is a rigid type variable bound by
the type signature for:
x_a8Yw :: forall a. a
```
It looks like the scoped `a` is transformed into a generalized `a` after splicing.
Looking at `-ddump-simpl -dsuppress-all`, it seems that the connection between the scoped `forall a.` and the `a` in `x :: a` is completely lost in the code for `f`, although GHC does know about it during type checking `f`'s definition. As far as I see, the same thing happens with all scoped variables in quotes.
One solution would be to have quoting for types. In hypothetical syntax:
```haskell
f :: forall (a : CodeQ Type). CodeQ $$a -> CodeQ $$a
f @a x = [|| let y :: $$a; y = $$x in y ||]
```
This one seems a rather big change but I'm not sure about other solutions which are perhaps more realistic to integrate to TH (I don't really know how splice processing works).https://gitlab.haskell.org/ghc/ghc/-/issues/21050Assertion failure with typed Template Haskell type variable2023-05-09T22:40:48ZKrzysztof GogolewskiAssertion failure with typed Template Haskell type variableFile:
```haskell
{-# LANGUAGE TemplateHaskell, RankNTypes #-}
module M where
import Language.Haskell.TH.Syntax
data T = MkT (forall a. a)
f x = [|| MkT $$(x) ||]
```
In 9.2 and earlier, this compiles, even with `-dcore-lint` enabled. B...File:
```haskell
{-# LANGUAGE TemplateHaskell, RankNTypes #-}
module M where
import Language.Haskell.TH.Syntax
data T = MkT (forall a. a)
f x = [|| MkT $$(x) ||]
```
In 9.2 and earlier, this compiles, even with `-dcore-lint` enabled. But the inferred type, `f :: Q (TExp a) -> Q (TExp T)`, is wrong; that'd be correct if `MkT :: a -> T` while `MkT :: (forall a. a) -> T`.
In master, this gives an assertion failure about levels inside `writeMetaTyVarRef`. If assertions are disabled, it fails Lint.
```
*** Core Lint errors : in result of Desugar (before optimization) ***
M.hs:6:1: warning:
The type variable @a_a1W4[sk:2] is out of scope
In the type of a binder: f
In the type ‘forall {m :: * -> *}.
Quote m =>
Code m a_a1W4[sk:2] -> Code m T’
Substitution: [TCvSubst
In scope: InScope {m_a1Ib}
Type env: [a1Ib :-> m_a1Ib]
Co env: []]
```
This looks related to #19893, but I'm not sure if it's the same bug.https://gitlab.haskell.org/ghc/ghc/-/issues/19893Core Lint error with typed Template Haskell (The type variable ... is out of ...2023-08-11T11:04:21ZRyan ScottCore Lint error with typed Template Haskell (The type variable ... is out of scope)The `parsley-0.1.0.1` Hackage library fails to compile with `-dcore-lint`. Here is a minimized version of the failing code:
```hs
{-# LANGUAGE TemplateHaskell #-}
module Bug where
import Control.Monad.ST
import Language.Haskell.TH
f :...The `parsley-0.1.0.1` Hackage library fails to compile with `-dcore-lint`. Here is a minimized version of the failing code:
```hs
{-# LANGUAGE TemplateHaskell #-}
module Bug where
import Control.Monad.ST
import Language.Haskell.TH
f :: Code Q Char
f = [|| runST $$([|| pure 'a' ||]) ||]
```
```
$ /opt/ghc/9.0.1/bin/ghc Bug.hs -dcore-lint
[1 of 1] Compiling Bug ( Bug.hs, Bug.o, Bug.dyn_o )
*** Core Lint errors : in result of Desugar (before optimization) ***
<no location info>: warning:
The type variable @s_a20Z[sk:1] is out of scope
In the type of a binder: $dApplicative_a218
In the type ‘Applicative (ST s_a20Z[sk:1])’
Substitution: [TCvSubst
In scope: InScope {}
Type env: []
Co env: []]
*** Offending Program ***
Rec {
$dQuote_a211 :: Quote Q
[LclId]
$dQuote_a211 = $dQuote_a20R
$dQuote_a20R :: Quote Q
[LclId]
$dQuote_a20R = $fQuoteQ
$dApplicative_a218 :: Applicative (ST s_a20Z[sk:1])
[LclId]
$dApplicative_a218 = $fApplicativeST @s_a20Z[sk:1]
$trModule :: Module
[LclIdX]
$trModule = Module (TrNameS "main"#) (TrNameS "Bug"#)
f :: Code Q Char
[LclIdX]
f = unsafeCodeCoerce
@'LiftedRep
@Char
@Q
$dQuote_a20R
(appE
@Q
$dQuote_a20R
(varE
@Q
$dQuote_a20R
(mkNameG_v
(unpackCString# "base"#)
(unpackCString# "GHC.ST"#)
(unpackCString# "runST"#)))
(unTypeCode
@'LiftedRep
@(ST s_a20Z[sk:1] Char)
@Q
$dQuote_a20R
(unsafeCodeCoerce
@'LiftedRep
@(ST s_a20Z[sk:1] Char)
@Q
$dQuote_a211
(appE
@Q
$dQuote_a211
(varE
@Q
$dQuote_a211
(mkNameG_v
(unpackCString# "base"#)
(unpackCString# "GHC.Base"#)
(unpackCString# "pure"#)))
(litE @Q $dQuote_a211 (charL (C# 'a'#)))))))
end Rec }
*** End of Offense ***
<no location info>: error:
Compilation had errors
```https://gitlab.haskell.org/ghc/ghc/-/issues/19368Typed Template Haskell suppresses -Wunused-top-binds warnings2021-02-21T15:24:30ZRyan ScottTyped Template Haskell suppresses -Wunused-top-binds warningsIn this program:
```hs
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wunused-top-binds #-}
module Bug (a) where
a :: Int
a = $([| 42 |])
-- a = $$([|| 42 ||])
z :: Int
z = 42
```
GHC correctly informs me that `z` is defined but n...In this program:
```hs
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wunused-top-binds #-}
module Bug (a) where
a :: Int
a = $([| 42 |])
-- a = $$([|| 42 ||])
z :: Int
z = 42
```
GHC correctly informs me that `z` is defined but not used:
```
$ /opt/ghc/8.10.3/bin/ghc Bug.hs
[1 of 1] Compiling Bug ( Bug.hs, Bug.o, Bug.dyn_o )
Bug.hs:10:1: warning: [-Wunused-top-binds]
Defined but not used: ‘z’
|
10 | z = 42
| ^
```
If I comment out the definition of `a` and replace it with `a = $$([|| 42 ||])`, however, then GHC doesn't report any warnings at all!
```
$ /opt/ghc/8.10.3/bin/ghc Bug.hs
[1 of 1] Compiling Bug ( Bug.hs, Bug.o, Bug.dyn_o )
```
I'm unclear why the use of typed Template Haskell would change things here, as I would expect `-Wunused-top-binds` warnings in both scenarios.https://gitlab.haskell.org/ghc/ghc/-/issues/18102TemplateHaskellQuotes and RebindableSyntax don't play nicely2021-02-05T10:11:11ZRichard Eisenbergrae@richarde.devTemplateHaskellQuotes and RebindableSyntax don't play nicelyEDIT: The original problem described here was about untyped TH, and it was fixed in !2960. But we identify a new problem with typed Template Haskell, which has yet to be solved. https://gitlab.haskell.org/ghc/ghc/-/issues/18102#note_2922...EDIT: The original problem described here was about untyped TH, and it was fixed in !2960. But we identify a new problem with typed Template Haskell, which has yet to be solved. https://gitlab.haskell.org/ghc/ghc/-/issues/18102#note_292247 shows two illustrative examples.
-------------------------------------------------------
If I say
```hs
{-# LANGUAGE TemplateHaskell, RebindableSyntax #-}
module Bug where
import Prelude ( Monad(..), Bool(..), print, ($) )
import Language.Haskell.TH.Syntax
$( do stuff <- [| if True then 10 else 15 |]
runIO $ print stuff
return [] )
```
then I get errors about `ifThenElse` and `fromInteger` not being in scope. When I bring them into scope, the code is accepted, but the quoted syntax does not mention them, instead accurately reflecting the original source Haskell I wrote.
There are several problems in this story:
1. Template Haskell quotes are meant to work even in the presence of unbound identifiers.
2. Template Haskell is all about quoting (and splicing) surface syntax. Quoting should not care about `-XRebindableSyntax`.
3. If we were to decide that quoting should respect `-XRebindableSyntax` (I disagree with this design, to be clear), then the output of the quote is incorrect.
I propose: disable `-XRebindableSyntax` within quotes. That should fix all these problems.
Other reasonable possibility: disable `-XRebindableSyntax` within quotes, but use the TH combinators that are in scope (like `conE` and `appE`, etc.). I think this would be nightmarish to implement, and no one is asking for it, but it would be a consistent way forward.https://gitlab.haskell.org/ghc/ghc/-/issues/17863Typed template haskell is very underdocumented2020-02-26T17:01:10ZadamTyped template haskell is very underdocumentedThere is a lack of documentation in both the [users guide][1] and the [template-haskell haddocks][2]. Every time I go looking it takes a while to find the 2 bullet points in the users guide on the syntax.
An own section and a worked exa...There is a lack of documentation in both the [users guide][1] and the [template-haskell haddocks][2]. Every time I go looking it takes a while to find the 2 bullet points in the users guide on the syntax.
An own section and a worked example would be a big improvement.
[1]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?highlight=typed%20expression#th-syntax
[2]: https://hackage.haskell.org/package/template-haskell-2.15.0.0/docs/Language-Haskell-TH.html#g:12https://gitlab.haskell.org/ghc/ghc/-/issues/16178Brackets and splices should be overloaded like the static keyword2019-07-07T18:01:06ZMatthew PickeringBrackets and splices should be overloaded like the static keywordIt's quite convenient that the `static` keyword is rebindable. To recap, if `e :: T` then `static e :: (IsStatic p) => p t`.
It should also be possible rebind brackets and splices in the same manner.
So we introduce two type classes `Is...It's quite convenient that the `static` keyword is rebindable. To recap, if `e :: T` then `static e :: (IsStatic p) => p t`.
It should also be possible rebind brackets and splices in the same manner.
So we introduce two type classes `IsBracket` and `IsSplice`. Now quoting a term `e :: T` has type `e :: IsBracket p => p T` and the argument to a splice
must have type `e :: IsSplice p => p T` which results in a value of type `T`.
```
class IsBracket p where
fromBracket :: Code t -> p t
class IsSplice p where
toBracket :: p t -> Code t
foo :: IsBracket p => p Int
foo = [|| 5 ||]
qux :: (IsSplice p, IsBracket p) => Int
qux = $$(foo)
```
As an aside, arguably the `static` form should only be rebindable when `RebindableSyntax` is enabled but that boat seems to have sailed.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ---------------- |
| Version | 8.6.3 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Template Haskell |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Brackets and splices should be overloaded like the static keyword","status":"New","operating_system":"","component":"Template Haskell","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.3","keywords":["TypedTemplateHaskell"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"FeatureRequest","description":"It's quite convenient that the `static` keyword is rebindable. To recap, if `e :: T` then `static e :: (IsStatic p) => p t`. \r\n\r\nIt should also be possible rebind brackets and splices in the same manner. \r\nSo we introduce two type classes `IsBracket` and `IsSplice`. Now quoting a term `e :: T` has type `e :: IsBracket p => p T` and the argument to a splice\r\nmust have type `e :: IsSplice p => p T` which results in a value of type `T`. \r\n\r\n{{{\r\nclass IsBracket p where\r\n fromBracket :: Code t -> p t\r\n\r\nclass IsSplice p where\r\n toBracket :: p t -> Code t\r\n\r\nfoo :: IsBracket p => p Int\r\nfoo = [|| 5 ||]\r\n\r\nqux :: (IsSplice p, IsBracket p) => Int\r\nqux = $$(foo)\r\n}}}\r\n\r\n\r\nAs an aside, arguably the `static` form should only be rebindable when `RebindableSyntax` is enabled but that boat seems to have sailed. ","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/16176Let-insertion for template haskell2019-07-07T18:01:07ZMatthew PickeringLet-insertion for template haskellWhen using Template Haskell to generate programs it's very easy to end up with a lot of duplication as splices are naively spliced in place.
For example
```
foo x = [|| $$x + $$x ||]
```
Will generate a program which completely duplic...When using Template Haskell to generate programs it's very easy to end up with a lot of duplication as splices are naively spliced in place.
For example
```
foo x = [|| $$x + $$x ||]
```
Will generate a program which completely duplicates its argument. In this case I can manually remove the duplicate by inserting a let.
```
foo x = [|| let x' = $$x in x' + x' ||]
```
Not too bad but a bit annoying to have to do manually.
When constructing bigger programs however this process becomes tedious or impossible to do correctly by hand.
```
foo :: (Q (TExp (Bool)) -> Q (TExp Int)) -> Q (TExp Int)
foo xf = [|| (let x = True in $$(xf [|| x ||])) + (let x = False in $$(xf [|| x ||]) ||]
```
Now if I pass a constant function to `foo`, the resulting code won't mention `x` so it could be floated out. However, there's not way I can tell that without running `xf` so I can't perform the same transformation as I did for the earlier program and manually insert a let. In the case of splicing in fully static data you really want it to float to the top-level and turn into a CAF.
The proposal of this ticket is to implement something like the mechanism for let-insertion in
metaocaml.
http://okmij.org/ftp/meta-programming/\#let-insert
We add two new primitives:
```
genlet :: Q (TExp a) -> Q (TExp a)
let_locus :: Q (TExp a) -> Q (TExp a)
```
`genlet` marks a code value that we want to float. `let_locus` marks places where we want to insert a let. When we evaluate the code fragment and encounter a `genlet` call, whatever the argument evaluates to is floated as far upwards as possible and inserted at the position of one of the loci.
For example,
```
sqr :: Code Int -> Code Int
sqr c = [|| $$c + $$c ||]
sqr_let :: Code Int -> Code Int
sqr_let c = let_locus (sqr (genlet c))
```
Splicing `sqr [|| 1 ||]` will result in `1 + 1` but `sqr_let [|| c ||]` will equal `let x = 1 in x + x ||]`.
It's important to do this earlier rather than later as a lot of duplication can take place which the simplifier does not like.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ---------------- |
| Version | 8.6.3 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Template Haskell |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Let-insertion for template haskell","status":"New","operating_system":"","component":"Template Haskell","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.3","keywords":["TypedTemplateHaskell"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"FeatureRequest","description":"When using Template Haskell to generate programs it's very easy to end up with a lot of duplication as splices are naively spliced in place. \r\n\r\nFor example\r\n\r\n{{{\r\nfoo x = [|| $$x + $$x ||]\r\n}}}\r\n\r\nWill generate a program which completely duplicates its argument. In this case I can manually remove the duplicate by inserting a let.\r\n\r\n{{{\r\nfoo x = [|| let x' = $$x in x' + x' ||]\r\n}}}\r\n\r\nNot too bad but a bit annoying to have to do manually. \r\n\r\nWhen constructing bigger programs however this process becomes tedious or impossible to do correctly by hand.\r\n\r\n{{{\r\nfoo :: (Q (TExp (Bool)) -> Q (TExp Int)) -> Q (TExp Int) \r\nfoo xf = [|| (let x = True in $$(xf [|| x ||])) + (let x = False in $$(xf [|| x ||]) ||]\r\n}}}\r\n\r\nNow if I pass a constant function to `foo`, the resulting code won't mention `x` so it could be floated out. However, there's not way I can tell that without running `xf` so I can't perform the same transformation as I did for the earlier program and manually insert a let. In the case of splicing in fully static data you really want it to float to the top-level and turn into a CAF.\r\n\r\nThe proposal of this ticket is to implement something like the mechanism for let-insertion in\r\nmetaocaml.\r\n\r\nhttp://okmij.org/ftp/meta-programming/#let-insert\r\n\r\nWe add two new primitives:\r\n\r\n{{{\r\ngenlet :: Q (TExp a) -> Q (TExp a)\r\nlet_locus :: Q (TExp a) -> Q (TExp a)\r\n}}}\r\n\r\n`genlet` marks a code value that we want to float. `let_locus` marks places where we want to insert a let. When we evaluate the code fragment and encounter a `genlet` call, whatever the argument evaluates to is floated as far upwards as possible and inserted at the position of one of the loci. \r\n\r\nFor example,\r\n\r\n{{{\r\nsqr :: Code Int -> Code Int\r\nsqr c = [|| $$c + $$c ||]\r\n\r\nsqr_let :: Code Int -> Code Int\r\nsqr_let c = let_locus (sqr (genlet c))\r\n}}}\r\n\r\nSplicing `sqr [|| 1 ||]` will result in `1 + 1` but `sqr_let [|| c ||]` will equal `let x = 1 in x + x ||]`. \r\n\r\nIt's important to do this earlier rather than later as a lot of duplication can take place which the simplifier does not like. ","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/16169Unused variable warning affects compositionality when generating code2019-10-03T08:46:45ZMatthew PickeringUnused variable warning affects compositionality when generating codeIf I'm generating a program using template haskell, I might define
a combinator to help me generate lambdas.
```
type Code a = Q (TExp a)
_lam :: (Code a -> Code b) -> Code (a -> b)
_lam f = [|| \a -> $$(f [|| a ||]) ||]
```
However, ...If I'm generating a program using template haskell, I might define
a combinator to help me generate lambdas.
```
type Code a = Q (TExp a)
_lam :: (Code a -> Code b) -> Code (a -> b)
_lam f = [|| \a -> $$(f [|| a ||]) ||]
```
However, if I now pass a constant function into `_lam`, the generated code contains an unused variable `a` that I can do nothing about and desire not to do anything about.
```
c5 :: Code (a -> Int)
c5 = _lam (const [|| 5 ||])
```
However, GHC decides that it's wise to warn me about this when I splice it in.
```
> $$c5
F2.hs:6:5: warning: [-Wunused-matches] Defined but not used: ‘a’
|
6 | q = $$c5
| ^^^
```
As Ryan will tell you, I'm against emitting warnings from generated code as it breaks the abstraction. The code that is generated is guaranteed to be type and scope correct but any aesthetic warning is irrelevant to the consumer.
I see it in precisely the same way as warning if we use a function that contains an unused variable in a library. Once the definition is exported from the library, its definition is opaque. The same principle should be applied to code generated by template haskell.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.6.3 |
| 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":"Unused variable warning affects compositionality when generating code","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"If I'm generating a program using template haskell, I might define\r\na combinator to help me generate lambdas. \r\n\r\n{{{\r\ntype Code a = Q (TExp a)\r\n\r\n_lam :: (Code a -> Code b) -> Code (a -> b)\r\n_lam f = [|| \\a -> $$(f [|| a ||]) ||]\r\n}}}\r\n\r\nHowever, if I now pass a constant function into `_lam`, the generated code contains an unused variable `a` that I can do nothing about and desire not to do anything about.\r\n\r\n{{{\r\nc5 :: Code (a -> Int)\r\nc5 = _lam (const [|| 5 ||])\r\n}}}\r\n\r\nHowever, GHC decides that it's wise to warn me about this when I splice it in.\r\n\r\n{{{\r\n> $$c5\r\nF2.hs:6:5: warning: [-Wunused-matches] Defined but not used: ‘a’\r\n |\r\n6 | q = $$c5\r\n | ^^^\r\n}}}\r\n\r\nAs Ryan will tell you, I'm against emitting warnings from generated code as it breaks the abstraction. The code that is generated is guaranteed to be type and scope correct but any aesthetic warning is irrelevant to the consumer. \r\n\r\nI see it in precisely the same way as warning if we use a function that contains an unused variable in a library. Once the definition is exported from the library, its definition is opaque. The same principle should be applied to code generated by template haskell. \r\n\r\n","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/15865Typed template haskell and implicit parameters lead to incorrect results2020-10-30T19:29:43ZMatthew PickeringTyped template haskell and implicit parameters lead to incorrect resultsIn a similar vein to #15863 but this time with implicit parameters.
https://gist.github.com/b6919b13abe0954fdad844e16e0edb48
```
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE TemplateHaskell #-}
module A where
import Language.Haskell.T...In a similar vein to #15863 but this time with implicit parameters.
https://gist.github.com/b6919b13abe0954fdad844e16e0edb48
```
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE TemplateHaskell #-}
module A where
import Language.Haskell.TH
import Data.List (sortBy)
sort :: (?cmp :: a -> a -> Ordering) => [a] -> [a]
sort = sortBy ?cmp
me :: Q (TExp ([Int] -> [Int]))
me = let ?cmp = compare in [|| sort ||]
```
In module `A` we quote a value which has an implicit argument but in its context we bind the implicit so the type of the quote is the monomorphic type.
```
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE TemplateHaskell #-}
module B where
import A
foo :: [Int] -> [Int]
foo =
--let ?cmp = compare in
$$(me)
```
When we splice in `me`, we get an error about an unbound implicit parameter which is totally bogus as we already bound it in `A`. There is also dynamic binding if another implicit parameter with the same name is in scope but the type of `me` mentions nothing about implicit parameters so this shouldn't be allowed.
```
B.hs:8:10: error:
• Unbound implicit parameter (?cmp::Int -> Int -> Ordering)
arising from a use of ‘sort’
• In the expression: sort
In the result of the splice:
$me
To see what the splice expanded to, use -ddump-splices
In the Template Haskell splice $$(me)
|
8 | foo = $$(me)
| ^^
Failed, one module loaded.
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.6.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Typed template haskell and implicit parameters lead to incorrect results","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"In a similar vein to #15863 but this time with implicit parameters.\r\n\r\nhttps://gist.github.com/b6919b13abe0954fdad844e16e0edb48\r\n\r\n{{{\r\n{-# LANGUAGE ImplicitParams #-}\r\n{-# LANGUAGE TemplateHaskell #-}\r\nmodule A where\r\n\r\nimport Language.Haskell.TH\r\nimport Data.List (sortBy)\r\n\r\n\r\nsort :: (?cmp :: a -> a -> Ordering) => [a] -> [a]\r\nsort = sortBy ?cmp\r\n\r\nme :: Q (TExp ([Int] -> [Int]))\r\nme = let ?cmp = compare in [|| sort ||]\r\n}}}\r\n\r\nIn module `A` we quote a value which has an implicit argument but in its context we bind the implicit so the type of the quote is the monomorphic type.\r\n\r\n{{{\r\n{-# LANGUAGE ImplicitParams #-}\r\n{-# LANGUAGE TemplateHaskell #-}\r\nmodule B where\r\n\r\nimport A\r\n\r\nfoo :: [Int] -> [Int]\r\nfoo =\r\n --let ?cmp = compare in\r\n $$(me)\r\n}}}\r\n\r\nWhen we splice in `me`, we get an error about an unbound implicit parameter which is totally bogus as we already bound it in `A`. There is also dynamic binding if another implicit parameter with the same name is in scope but the type of `me` mentions nothing about implicit parameters so this shouldn't be allowed.\r\n\r\n{{{\r\nB.hs:8:10: error:\r\n • Unbound implicit parameter (?cmp::Int -> Int -> Ordering)\r\n arising from a use of ‘sort’\r\n • In the expression: sort\r\n In the result of the splice:\r\n $me\r\n To see what the splice expanded to, use -ddump-splices\r\n In the Template Haskell splice $$(me)\r\n |\r\n8 | foo = $$(me)\r\n | ^^\r\nFailed, one module loaded.\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/15863Splicing a type class method selects the wrong instance2019-07-07T18:02:40ZMatthew PickeringSplicing a type class method selects the wrong instanceConsider these 4 modules as concocted by Csongor.
The wrong instance is selected when you splice in `B.me` into `D`.
https://gist.github.com/mpickering/959a95525647802414ab50e8e6ed490c
```
module A where
class C a where
foo :: a ->...Consider these 4 modules as concocted by Csongor.
The wrong instance is selected when you splice in `B.me` into `D`.
https://gist.github.com/mpickering/959a95525647802414ab50e8e6ed490c
```
module A where
class C a where
foo :: a -> String
instance C Int where
foo _ = "int"
```
```
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
module B where
import A
import Language.Haskell.TH
instance C a => C [a] where
foo _ = "list"
me :: Q (TExp ([Int] -> String))
me = [|| foo ||]
```
```
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
module C where
import A
import Language.Haskell.TH
instance {-# OVERLAPPING #-} C [Int] where
foo _ = "list2"
```
```
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
module D where
import A
import B
import C
main2 = $$(me) [1 :: Int]
```
```
>>> main2
"list2"
```
In `B`, `B.me` is created by quoting `foo`. `B.me :: Q (TExp ([Int] -> String))` so in order to type check this quote we need to solve the instance `C [Int]` which we should do by using the instance defined in `B` (and `A`).
In module `C` we define a different overlapping instance (note that this could be in a completely different library not under our control).
When we then splice `B.me` into `D`, the instance from `C` is used and can be witnessed by printing `main2` which shows `"list2"` rather than `"list"` as expected.
This is a symptom of the fact that the renamed rather than the typechecked AST is serialised I think.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.6.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Splcing a type class method selects the wrong instance","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Consider these 4 modules as concocted by Csongor. \r\n\r\nThe wrong instance is selected when you splice in `B.me` into `D`.\r\n\r\nhttps://gist.github.com/mpickering/959a95525647802414ab50e8e6ed490c\r\n\r\n{{{\r\nmodule A where\r\n\r\nclass C a where\r\n foo :: a -> String\r\n\r\ninstance C Int where\r\nfoo _ = \"int\"\r\n}}}\r\n\r\n{{{\r\n{-# LANGUAGE FlexibleInstances #-}\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE NoMonomorphismRestriction #-}\r\nmodule B where\r\n\r\nimport A\r\n\r\nimport Language.Haskell.TH\r\n\r\ninstance C a => C [a] where\r\n foo _ = \"list\"\r\n\r\nme :: Q (TExp ([Int] -> String))\r\nme = [|| foo ||]\r\n}}}\r\n\r\n{{{\r\n{-# LANGUAGE FlexibleInstances #-}\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE NoMonomorphismRestriction #-}\r\nmodule C where\r\n\r\nimport A\r\n\r\nimport Language.Haskell.TH\r\n\r\ninstance {-# OVERLAPPING #-} C [Int] where\r\nfoo _ = \"list2\"\r\n}}}\r\n\r\n{{{\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE NoMonomorphismRestriction #-}\r\nmodule D where\r\n\r\nimport A\r\nimport B\r\nimport C\r\n\r\nmain2 = $$(me) [1 :: Int]\r\n}}}\r\n\r\n{{{\r\n>>> main2\r\n\"list2\"\r\n}}}\r\n\r\n\r\nIn `B`, `B.me` is created by quoting `foo`. `B.me :: Q (TExp ([Int] -> String))` so in order to type check this quote we need to solve the instance `C [Int]` which we should do by using the instance defined in `B` (and `A`). \r\n\r\nIn module `C` we define a different overlapping instance (note that this could be in a completely different library not under our control).\r\n\r\nWhen we then splice `B.me` into `D`, the instance from `C` is used and can be witnessed by printing `main2` which shows `\"list2\"` rather than `\"list\"` as expected.\r\n\r\nThis is a symptom of the fact that the renamed rather than the typechecked AST is serialised I think.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/15835Internal error when splicing value constructed using typed template haskell2019-07-07T18:02:47ZMatthew PickeringInternal error when splicing value constructed using typed template haskellRelated to #15833
Compiling Test.hs leads to an internal compiler error.
https://gist.github.com/f04a613bb5e20c241c5b91c2f38b8f06
```
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -ddump-splices #-}
module Test where
import qualif...Related to #15833
Compiling Test.hs leads to an internal compiler error.
https://gist.github.com/f04a613bb5e20c241c5b91c2f38b8f06
```
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -ddump-splices #-}
module Test where
import qualified Compiler as C
main :: IO ()
main = $$(C.runQuery)
```
```
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wall #-}
module Compiler where
import Language.Haskell.TH
type QTExp a = Q (TExp a)
fix :: (a -> a) -> a
fix f = let x = f x in x
while ::
forall m . Monoid m =>
QTExp (IO m -> IO m) -> QTExp (IO m)
while b = [|| fix (\r -> whenM @(IO m) ($$b r)) ||]
whenM :: Monoid m => m -> m
whenM _ = mempty
execOp :: forall m . Monoid m => QTExp (IO m)
execOp = while [|| \r -> $$(while @m [|| id ||]) >> r ||]
runQuery :: QTExp (IO ())
runQuery = execOp
```
Leads to the following internal errors even though `Compiler` type checked.
```
Prelude> :r
[1 of 2] Compiling Compiler ( Compiler.hs, interpreted )
[2 of 2] Compiling Test ( Test.hs, interpreted )
Test.hs:9:6-15: Splicing expression
C.runQuery
======>
C.fix
(\ r_a7K7
-> (C.whenM @(IO m_a7Gp))
((\ r_a7K8
-> ((C.fix (\ r_a7K9 -> (C.whenM @(IO m_a7Gp)) (id r_a7K9)))
>> r_a7K8))
r_a7K7))
Test.hs:9:6: error:
• The exact Name ‘m’ is not in scope
Probable cause: you used a unique Template Haskell name (NameU),
perhaps via newName, but did not bind it
If that's it, then -ddump-splices might be useful
• In the result of the splice:
$C.runQuery
To see what the splice expanded to, use -ddump-splices
In the Template Haskell splice $$(C.runQuery)
In a stmt of a 'do' block: $$(C.runQuery)
|
9 | $$(C.runQuery)
| ^^^^^^^^^^
Test.hs:9:6: error:
• The exact Name ‘m’ is not in scope
Probable cause: you used a unique Template Haskell name (NameU),
perhaps via newName, but did not bind it
If that's it, then -ddump-splices might be useful
• In the result of the splice:
$C.runQuery
To see what the splice expanded to, use -ddump-splices
In the Template Haskell splice $$(C.runQuery)
In a stmt of a 'do' block: $$(C.runQuery)
|
9 | $$(C.runQuery)
| ^^^^^^^^^^
Test.hs:9:6: error:
• GHC internal error: ‘m’ is not in scope during type checking, but it passed the renamer
tcl_env of environment: [a7K7 :-> Identifier[r_a7K7::a0, NotLetBound],
r5Fg :-> Identifier[main::IO (), TopLevelLet [] True]]
• In the first argument of ‘IO’, namely ‘m’
In the type ‘(IO m)’
In the expression:
(C.whenM @(IO m))
((\ r_a7K8
-> ((C.fix (\ r_a7K9 -> (C.whenM @(IO m)) (id r_a7K9))) >> r_a7K8))
r_a7K7)
|
9 | $$(C.runQuery)
|
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.6.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Internal error when splicing value constructed using typed template haskell","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Related to #15833\r\n\r\nCompiling Test.hs leads to an internal compiler error. \r\n\r\nhttps://gist.github.com/f04a613bb5e20c241c5b91c2f38b8f06\r\n\r\n{{{\r\n\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# OPTIONS_GHC -ddump-splices #-}\r\nmodule Test where\r\n\r\nimport qualified Compiler as C\r\n\r\nmain :: IO ()\r\nmain = do\r\n$$(C.runQuery)\r\n}}}\r\n\r\n{{{\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE ScopedTypeVariables #-}\r\n{-# LANGUAGE TypeApplications #-}\r\n{-# OPTIONS_GHC -Wall #-}\r\nmodule Compiler where\r\n\r\nimport Language.Haskell.TH\r\n\r\ntype QTExp a = Q (TExp a)\r\n\r\nfix :: (a -> a) -> a\r\nfix f = let x = f x in x\r\n\r\nwhile ::\r\n forall m . Monoid m =>\r\n QTExp (IO m -> IO m) -> QTExp (IO m)\r\nwhile b = [|| fix (\\r -> whenM @(IO m) ($$b r)) ||]\r\n\r\nwhenM :: Monoid m => m -> m\r\nwhenM _ = mempty\r\n\r\nexecOp :: forall m . Monoid m => QTExp (IO m)\r\nexecOp = while [|| \\r -> $$(while @m [|| id ||]) >> r ||]\r\n\r\nrunQuery :: QTExp (IO ())\r\nrunQuery = execOp\r\n}}}\r\n\r\n\r\n\r\nLeads to the following internal errors even though `Compiler` type checked.\r\n\r\n{{{\r\nPrelude> :r\r\n[1 of 2] Compiling Compiler ( Compiler.hs, interpreted )\r\n[2 of 2] Compiling Test ( Test.hs, interpreted )\r\nTest.hs:9:6-15: Splicing expression\r\n C.runQuery\r\n ======>\r\n C.fix\r\n (\\ r_a7K7\r\n -> (C.whenM @(IO m_a7Gp))\r\n ((\\ r_a7K8\r\n -> ((C.fix (\\ r_a7K9 -> (C.whenM @(IO m_a7Gp)) (id r_a7K9)))\r\n >> r_a7K8))\r\n r_a7K7))\r\n\r\nTest.hs:9:6: error:\r\n • The exact Name ‘m’ is not in scope\r\n Probable cause: you used a unique Template Haskell name (NameU), \r\n perhaps via newName, but did not bind it\r\n If that's it, then -ddump-splices might be useful\r\n • In the result of the splice:\r\n $C.runQuery\r\n To see what the splice expanded to, use -ddump-splices\r\n In the Template Haskell splice $$(C.runQuery)\r\n In a stmt of a 'do' block: $$(C.runQuery)\r\n |\r\n9 | $$(C.runQuery)\r\n | ^^^^^^^^^^\r\n\r\nTest.hs:9:6: error:\r\n • The exact Name ‘m’ is not in scope\r\n Probable cause: you used a unique Template Haskell name (NameU), \r\n perhaps via newName, but did not bind it\r\n If that's it, then -ddump-splices might be useful\r\n • In the result of the splice:\r\n $C.runQuery\r\n To see what the splice expanded to, use -ddump-splices\r\n In the Template Haskell splice $$(C.runQuery)\r\n In a stmt of a 'do' block: $$(C.runQuery)\r\n |\r\n9 | $$(C.runQuery)\r\n | ^^^^^^^^^^\r\n\r\nTest.hs:9:6: error:\r\n • GHC internal error: ‘m’ is not in scope during type checking, but it passed the renamer\r\n tcl_env of environment: [a7K7 :-> Identifier[r_a7K7::a0, NotLetBound],\r\n r5Fg :-> Identifier[main::IO (), TopLevelLet [] True]]\r\n • In the first argument of ‘IO’, namely ‘m’\r\n In the type ‘(IO m)’\r\n In the expression:\r\n (C.whenM @(IO m))\r\n ((\\ r_a7K8\r\n -> ((C.fix (\\ r_a7K9 -> (C.whenM @(IO m)) (id r_a7K9))) >> r_a7K8))\r\n r_a7K7)\r\n |\r\n9 | $$(C.runQuery)\r\n | \r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->
https://gitlab.haskell.org/ghc/ghc/-/issues/15833Typed template haskell quote fails to typecheck when spliced due to an ambigu...2019-07-07T18:02:48ZMatthew PickeringTyped template haskell quote fails to typecheck when spliced due to an ambiguous type variableIt should be the case that a code value constructed using typed template haskell should never fail to type check when spliced. Running `ghc Test.hs` with the following two modules produces an error about an ambiguous type variable.
http...It should be the case that a code value constructed using typed template haskell should never fail to type check when spliced. Running `ghc Test.hs` with the following two modules produces an error about an ambiguous type variable.
https://gist.github.com/5890c14dda73da738d2041c7f677b786
```
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wall #-}
module Compiler where
import Language.Haskell.TH
data Operator = Scan
| Join Operator Operator deriving Show
queryJoin :: Operator
queryJoin = Join Scan Scan
type QTExp a = Q (TExp a)
fix :: (a -> a) -> a
fix f = let x = f x in x
while ::
Monoid m =>
QTExp (IO m -> IO m) -> QTExp (IO m)
while b = [|| fix (\r -> whenM True ($$b r)) ||]
whenM :: Monoid m => Bool -> m -> m
whenM b act = if b then act else mempty
execOp :: Monoid m => Operator -> QTExp (IO m) -> QTExp (IO m)
execOp op yld =
case op of
Scan ->
while [|| \r -> ($$(yld) >> r)||]
Join left right ->
execOp left (execOp right yld)
runQuery :: QTExp (IO ())
runQuery = execOp (Join Scan Scan) ([|| return () ||])
```
```
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -ddump-splices #-}
module Test where
import qualified Compiler as C
main :: IO ()
main = do
$$(C.runQuery)
```
```
Test.hs:9:6: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘C.whenM’
prevents the constraint ‘(Monoid a0)’ from being solved.
Relevant bindings include r_a5GX :: IO a0 (bound at Test.hs:9:6)
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’
instance Monoid Ordering -- Defined in ‘GHC.Base’
instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’
...plus 7 others
(use -fprint-potential-instances to see them all)
• In the expression:
(C.whenM True) ((\ r_a5GY -> ((return ()) >> r_a5GY)) r_a5GX)
In the first argument of ‘C.fix’, namely
‘(\ r_a5GX
-> (C.whenM True) ((\ r_a5GY -> ((return ()) >> r_a5GY)) r_a5GX))’
In the first argument of ‘(>>)’, namely
‘(C.fix
(\ r_a5GX
-> (C.whenM True) ((\ r_a5GY -> ((return ()) >> r_a5GY)) r_a5GX)))’
|
9 | $$(C.runQuery)
|
```
The generated code
```
Test.hs:9:6-15: Splicing expression
C.runQuery
======>
C.fix
(\ r_a5GV
-> (C.whenM True)
((\ r_a5GW
-> ((C.fix
(\ r_a5GX
-> (C.whenM True)
((\ r_a5GY -> ((return GHC.Tuple.()) >> r_a5GY)) r_a5GX)))
>> r_a5GW))
r_a5GV))
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.6.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Typed template haskell quote fails to typecheck when spliced due to an ambiguous type variable","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"It should be the case that a code value constructed using typed template haskell should never fail to type check when spliced. Running `ghc Test.hs` with the following two modules produces an error about an ambiguous type variable. \r\n\r\nhttps://gist.github.com/5890c14dda73da738d2041c7f677b786\r\n\r\n\r\n{{{\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# OPTIONS_GHC -Wall #-}\r\nmodule Compiler where\r\n\r\nimport Language.Haskell.TH\r\n\r\ndata Operator = Scan\r\n | Join Operator Operator deriving Show\r\n\r\nqueryJoin :: Operator\r\nqueryJoin = Join Scan Scan\r\n\r\ntype QTExp a = Q (TExp a)\r\n\r\nfix :: (a -> a) -> a\r\nfix f = let x = f x in x\r\n\r\nwhile ::\r\n Monoid m =>\r\n QTExp (IO m -> IO m) -> QTExp (IO m)\r\nwhile b = [|| fix (\\r -> whenM True ($$b r)) ||]\r\n\r\nwhenM :: Monoid m => Bool -> m -> m\r\nwhenM b act = if b then act else mempty\r\n\r\nexecOp :: Monoid m => Operator -> QTExp (IO m) -> QTExp (IO m)\r\nexecOp op yld =\r\n case op of\r\n Scan ->\r\n while [|| \\r -> ($$(yld) >> r)||]\r\n Join left right ->\r\n execOp left (execOp right yld)\r\n\r\nrunQuery :: QTExp (IO ())\r\nrunQuery = execOp (Join Scan Scan) ([|| return () ||])\r\n}}}\r\n\r\n{{{\r\n\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# OPTIONS_GHC -ddump-splices #-}\r\nmodule Test where\r\n\r\nimport qualified Compiler as C\r\n\r\nmain :: IO ()\r\nmain = do\r\n$$(C.runQuery)\r\n\r\n}}}\r\n\r\n{{{\r\nTest.hs:9:6: error:\r\n • Ambiguous type variable ‘a0’ arising from a use of ‘C.whenM’\r\n prevents the constraint ‘(Monoid a0)’ from being solved.\r\n Relevant bindings include r_a5GX :: IO a0 (bound at Test.hs:9:6)\r\n Probable fix: use a type annotation to specify what ‘a0’ should be.\r\n These potential instances exist:\r\n instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’\r\n instance Monoid Ordering -- Defined in ‘GHC.Base’\r\n instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’\r\n ...plus 7 others\r\n (use -fprint-potential-instances to see them all)\r\n • In the expression:\r\n (C.whenM True) ((\\ r_a5GY -> ((return ()) >> r_a5GY)) r_a5GX)\r\n In the first argument of ‘C.fix’, namely\r\n ‘(\\ r_a5GX\r\n -> (C.whenM True) ((\\ r_a5GY -> ((return ()) >> r_a5GY)) r_a5GX))’\r\n In the first argument of ‘(>>)’, namely\r\n ‘(C.fix\r\n (\\ r_a5GX\r\n -> (C.whenM True) ((\\ r_a5GY -> ((return ()) >> r_a5GY)) r_a5GX)))’\r\n |\r\n9 | $$(C.runQuery)\r\n | \r\n}}}\r\n\r\nThe generated code\r\n\r\n{{{\r\nTest.hs:9:6-15: Splicing expression\r\n C.runQuery\r\n ======>\r\n C.fix\r\n (\\ r_a5GV\r\n -> (C.whenM True)\r\n ((\\ r_a5GW\r\n -> ((C.fix\r\n (\\ r_a5GX\r\n -> (C.whenM True)\r\n ((\\ r_a5GY -> ((return GHC.Tuple.()) >> r_a5GY)) r_a5GX)))\r\n >> r_a5GW))\r\n r_a5GV))\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/13587addTopDecls fails with typed Template Haskell2019-07-07T18:21:09ZTrevor L. McDonelladdTopDecls fails with typed Template HaskellThe following untyped Template Haskell works as expected:
```hs
--- AddTopDecls.hs ---
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
module AddTopDecls where
import Language.Haskell.TH
import Language.Haskell.TH.Sy...The following untyped Template Haskell works as expected:
```hs
--- AddTopDecls.hs ---
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
module AddTopDecls where
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
importDoubleToDouble :: String -> ExpQ
importDoubleToDouble fname = do
n <- newName fname
d <- forImpD CCall unsafe fname n [t|Double -> Double|]
addTopDecls [d]
varE n
--- Main.hs ---
{-# LANGUAGE TemplateHaskell #-}
module Main where
import AddTopDecls
main :: IO ()
main = do
let sin' = $(importDoubleToDouble "sin")
cos' = $(importDoubleToDouble "cos")
--
print (sin' 0)
print (cos' pi)
```
However it fails if I convert to the equivalent typed version:
```hs
--- AddTopDecls.hs ---
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
module AddTopDecls where
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
importDoubleToDouble :: String -> Q (TExp (Double -> Double))
importDoubleToDouble fname = do
n <- newName fname
d <- forImpD CCall unsafe fname n [t|Double -> Double|]
addTopDecls [d]
unsafeTExpCoerce (varE n)
--- Main.hs ---
{-# LANGUAGE TemplateHaskell #-}
module Main where
import AddTopDecls
main :: IO ()
main = do
let sin' = $$(importDoubleToDouble "sin")
cos' = $$(importDoubleToDouble "cos")
--
print (sin' 0)
print (cos' pi)
```
With the error:
```
> ghci Main.hs -ddump-splices
GHCi, version 8.2.0.20170404: http://www.haskell.org/ghc/ :? for help
[1 of 2] Compiling AddTopDecls ( AddTopDecls.hs, interpreted )
[2 of 2] Compiling Main ( Main.hs, interpreted )
Main.hs:9:19-44: Splicing expression
importDoubleToDouble "sin" ======> sin_a4s2
Main.hs:1:1: Splicing top-level declarations added with addTopDecls
======>
foreign import ccall unsafe "sin" Main.sin :: Double -> Double
Main.hs:9:19: error:
• GHC internal error: ‘sin_a4s2’ is not in scope during type checking, but it passed the renamer
tcl_env of environment: [a4dl :-> Identifier[sin'::t1, TopLevelLet [] False],
a4dm :-> Identifier[cos'::t1, TopLevelLet [] False],
r4cW :-> Identifier[main::IO (), TopLevelLet]]
• In the expression: sin_a4s2
In the result of the splice:
$importDoubleToDouble "sin"
To see what the splice expanded to, use -ddump-splices
In the Template Haskell splice $$(importDoubleToDouble "sin")
|
9 | let sin' = $$(importDoubleToDouble "sin")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
```
Tested with 7.10.3, 8.0.2, and 8.2.0-rc1.
Unfortunately I can't use untyped TH in my real use case, so if you have any suggestions for a workaround that would also be great.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ---------------- |
| Version | 8.2.1-rc1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Template Haskell |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"addTopDecls fails with typed Template Haskell","status":"New","operating_system":"","component":"Template Haskell","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.2.1-rc1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"The following untyped Template Haskell works as expected:\r\n\r\n{{{#!hs\r\n--- AddTopDecls.hs ---\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE QuasiQuotes #-}\r\n\r\nmodule AddTopDecls where\r\n\r\nimport Language.Haskell.TH\r\nimport Language.Haskell.TH.Syntax\r\n\r\nimportDoubleToDouble :: String -> ExpQ\r\nimportDoubleToDouble fname = do\r\n n <- newName fname\r\n d <- forImpD CCall unsafe fname n [t|Double -> Double|]\r\n addTopDecls [d]\r\n varE n\r\n\r\n--- Main.hs ---\r\n{-# LANGUAGE TemplateHaskell #-}\r\n\r\nmodule Main where\r\n\r\nimport AddTopDecls\r\n\r\nmain :: IO ()\r\nmain = do\r\n let sin' = $(importDoubleToDouble \"sin\")\r\n cos' = $(importDoubleToDouble \"cos\")\r\n --\r\n print (sin' 0)\r\n print (cos' pi)\r\n}}}\r\n\r\nHowever it fails if I convert to the equivalent typed version:\r\n\r\n{{{#!hs\r\n--- AddTopDecls.hs ---\r\n{-# LANGUAGE TemplateHaskell #-}\r\n{-# LANGUAGE QuasiQuotes #-}\r\n\r\nmodule AddTopDecls where\r\n\r\nimport Language.Haskell.TH\r\nimport Language.Haskell.TH.Syntax\r\n\r\nimportDoubleToDouble :: String -> Q (TExp (Double -> Double))\r\nimportDoubleToDouble fname = do\r\n n <- newName fname\r\n d <- forImpD CCall unsafe fname n [t|Double -> Double|]\r\n addTopDecls [d]\r\n unsafeTExpCoerce (varE n)\r\n\r\n--- Main.hs ---\r\n{-# LANGUAGE TemplateHaskell #-}\r\n\r\nmodule Main where\r\n\r\nimport AddTopDecls\r\n\r\nmain :: IO ()\r\nmain = do\r\n let sin' = $$(importDoubleToDouble \"sin\")\r\n cos' = $$(importDoubleToDouble \"cos\")\r\n --\r\n print (sin' 0)\r\n print (cos' pi)\r\n}}}\r\n\r\nWith the error:\r\n\r\n{{{\r\n> ghci Main.hs -ddump-splices\r\nGHCi, version 8.2.0.20170404: http://www.haskell.org/ghc/ :? for help\r\n[1 of 2] Compiling AddTopDecls ( AddTopDecls.hs, interpreted )\r\n[2 of 2] Compiling Main ( Main.hs, interpreted )\r\nMain.hs:9:19-44: Splicing expression\r\n importDoubleToDouble \"sin\" ======> sin_a4s2\r\nMain.hs:1:1: Splicing top-level declarations added with addTopDecls\r\n ======>\r\n foreign import ccall unsafe \"sin\" Main.sin :: Double -> Double\r\n\r\nMain.hs:9:19: error:\r\n • GHC internal error: ‘sin_a4s2’ is not in scope during type checking, but it passed the renamer\r\n tcl_env of environment: [a4dl :-> Identifier[sin'::t1, TopLevelLet [] False],\r\n a4dm :-> Identifier[cos'::t1, TopLevelLet [] False],\r\n r4cW :-> Identifier[main::IO (), TopLevelLet]]\r\n • In the expression: sin_a4s2\r\n In the result of the splice:\r\n $importDoubleToDouble \"sin\"\r\n To see what the splice expanded to, use -ddump-splices\r\n In the Template Haskell splice $$(importDoubleToDouble \"sin\")\r\n |\r\n9 | let sin' = $$(importDoubleToDouble \"sin\")\r\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n}}}\r\n\r\nTested with 7.10.3, 8.0.2, and 8.2.0-rc1.\r\n\r\nUnfortunately I can't use untyped TH in my real use case, so if you have any suggestions for a workaround that would also be great.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/10271Typed Template Haskell splice difficulty when resolving overloading2020-08-25T09:55:38ZSimon Peyton JonesTyped Template Haskell splice difficulty when resolving overloadingJ Garrett Morris describes the following surprising behaviour for typed Template Haskell
```
{-# LANGUAGE TemplateHaskell, FlexibleInstances #-}
module PrintfLib where
import Language.Haskell.TH
class Printf t where
print...J Garrett Morris describes the following surprising behaviour for typed Template Haskell
```
{-# LANGUAGE TemplateHaskell, FlexibleInstances #-}
module PrintfLib where
import Language.Haskell.TH
class Printf t where
printf :: String -> Q (TExp String) -> Q (TExp t)
instance Printf [Char] where
tf s t | '%' `notElem` s = [|| $$t ++ s ||]
| otherwise = fail ("Unexpected format %"
++ [c])
where (_, _:c:_) = break ('%' ==) s
instance Printf t => Printf (Char -> t) where
printf s t
| c /= 'c' = fail ("Unexpected format %" ++ [c] ++
" for character")
| otherwise = [|| \c -> $$(printf s''
[|| $$t ++ s' ++ [c] ||])
||]
where (s', '%':c:s'') = break ('%' ==) s
-------------------------
{-# LANGUAGE TemplateHaskell #-}
module Printf where
import PrintfLib
f :: Char -> String
f = $$(printf "foo %c" [||""||])
h :: Char -> String
h y = $$(printf "foo %c" [||""||]) y
```
Oddly, `f` typechecks but `h` does not, even though `h` is just an eta-expanded version of `f`:
```
Printf.hs:9:10:
No instance for (Printf t0) arising from a use of ‘printf’
The type variable ‘t0’ is ambiguous
Note: there are several potential instances:
instance Printf t => Printf (Char -> t) -- Defined in ‘PrintfLib’
instance Printf [Char] -- Defined in ‘PrintfLib’
In the expression: printf "foo %c" [|| "" ||]
```
What is going on? Here's the deal
- To run the splice, GHC must solve any constraints that arise form the expression `(printf "foo %c" ...)`.
- Since `printf` is overloaded, and overloaded on its result type, the type needed by the context of the splice is what determines which instance of `Printf` is needed.
- In `f` the context needs `Char -> String`, and so the call to `printf` must have type `TExpr (Char -> String)`, so we get the constraint `Printf (Char -> String)` which we can solve.
- But in `h` the rule for application tries to *infer* a type for the splice. So the context for the call just says `TExp t0` for some unification variable `t0`; and that leads to the insoluble constraint.
You may say that GHC should be cleverer, and push that type signature information into the application. And perhaps it should. But you can never win. For example:
```
hard x = [ $$(printf "gluk" [|| "" ||]), undefined :: Char -> String ]
```
Here the RHS of `hard` is a 2-element list. Since all elements of a list have the same type, the splice must have the same type as the second element of the list, namely `Char->String`. But seeing that would mean that we'd have to typecheck right-to-left. In general GHC tries very very hard NOT to depend on traversal order. There is no way in general to ensure that we have all the information now that constraint solving may ultimately produce.
I'm not sure what to do about this.
- It seldom matters, because resolving the overloading on the splice seldom depends on the result type.
- When it does matter, you can fix it by giving a type signature to the splice itself.
But it seems unsatisfactory. Ideas welcome.https://gitlab.haskell.org/ghc/ghc/-/issues/22056panic! no skolem info when using typed template haskell2022-08-16T15:49:59Zoberblastmeisterpanic! no skolem info when using typed template haskell## Summary
When using typed template haskell with equality constraints GHC panics.
## Steps to reproduce
```haskell
{-# LANGUAGE TemplateHaskell #-}
module A where
import Language.Haskell.TH
bomb :: Code Q Int
bomb = [||1||]
```
```...## Summary
When using typed template haskell with equality constraints GHC panics.
## Steps to reproduce
```haskell
{-# LANGUAGE TemplateHaskell #-}
module A where
import Language.Haskell.TH
bomb :: Code Q Int
bomb = [||1||]
```
```haskell
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE GADTs #-}
module B where
import A
boom :: (a ~ Int) => a
boom = $$(bomb)
```
## Expected behavior
GHC should not panic.
## Environment
* GHC version used: 9.2.3
Optional:
* Operating System: NixOS
* System Architecture: x86_64https://gitlab.haskell.org/ghc/ghc/-/issues/21051Stage restriction differs between untyped and typed TH2022-02-08T12:50:31ZKrzysztof GogolewskiStage restriction differs between untyped and typed THFile:
```haskell
{-# LANGUAGE TemplateHaskell, TypeApplications #-}
module M where
data T
x = $(const @_ @T [| 'a' |] undefined)
y = $$(const @_ @T [|| 'a' ||] undefined)
```
`x` is rejected:
```
• GHC stage restriction:
...File:
```haskell
{-# LANGUAGE TemplateHaskell, TypeApplications #-}
module M where
data T
x = $(const @_ @T [| 'a' |] undefined)
y = $$(const @_ @T [|| 'a' ||] undefined)
```
`x` is rejected:
```
• GHC stage restriction:
‘T’ is used in a top-level splice, quasi-quote, or annotation,
and must be imported, not defined locally
```
But `y` is accepted. Is there any reason why untyped and typed TH behave differently?
I don't have any example where this actually goes wrong. If there's a reason, it might be added to the documentation.https://gitlab.haskell.org/ghc/ghc/-/issues/17804Ideas about testing and verifying code generated by typed template haskell2021-03-05T04:14:05ZMatthew PickeringIdeas about testing and verifying code generated by typed template haskellIn the recent practical experience of myself, Jamie and Andes we have felt the need for some more support for *verifing* that the programs that we generate using typed template haskell adhere to certain properties we had in mind.
This ...In the recent practical experience of myself, Jamie and Andes we have felt the need for some more support for *verifing* that the programs that we generate using typed template haskell adhere to certain properties we had in mind.
This ticket is a public place to write down some of these ideas so they are not lost forever.
## Size of subtrees
We want to be able to assert, at compile time, that the size of the generated code bares some known relationship to a statically known input. For example, the generated code is linear in the size of the input list.
In order to implement this we need the compiler to be able to report the size of a quotation. If the size of a quotation can be
inspected then the assertion functionality can be built into a library.
## Certain constructors are not present
We want to assert that in generated code certain constructors are not present. In particular, asserting that generic constructors never appear is an important property. In our own closed programs you can know this with reasonable confidence but in a hypothetical open world where people provide more staged interfaces, a rogue library may inefficiently implement an operation.
This would be a limited analysis at first because you can easily call already compiled functions in your generated code but can't inspect their definition.
## Thresholds for generating programs
A common mistake that people make when writing a staged program is to create an infinite compile time loop which generates an infinitely sized program rather than generating a loop which executes at runtime. This is much easier to do than when writing normal programs. It would be good if there was a limit as to how much recursion you were allowed to do at compile time so that beginners can get a good warning rather than puzzle, like we all did, about why the program is looping forever.
Other compile time evaluation features in GHC such as type class resolution and the inliner already have these warnings built in.
## Warning for CSP by lifting
In general, cross-stage persistence is bad. It makes it easy to accidentally create bloated programs if you accidentally use a variable at the wrong stage. For advanced users it should be possible to at least issue a warning if your program contains a cross-stage reference as you probably didn't mean it. If you did mean it, write it using an explicit splice and lift.
## Use overloaded quotes in order to perform analysis at runtime?
In order to perform some other verification it could be possible to instead perform runtime code generation by using overloaded quotations and inspecting the generating `Exp` value.https://gitlab.haskell.org/ghc/ghc/-/issues/17212Allow default methods to be implemented using template haskell2023-06-12T14:48:02ZMatthew PickeringAllow default methods to be implemented using template haskellIn conversations with @kosmikus he suggested that it would be nice to integrate default implementations with typed template haskell.
For example,
```
class Eq a where
eq :: a -> a -> Bool
default splice eq :: Generic a => Cod...In conversations with @kosmikus he suggested that it would be nice to integrate default implementations with typed template haskell.
For example,
```
class Eq a where
eq :: a -> a -> Bool
default splice eq :: Generic a => Code (a -> a -> Bool)
eq = _impl_
```
This has the massive advantage that the default implementation can be efficient than the usual `Data` or `Generics` based implementations.
There would need to be a GHC proposal related to this ticket but I write it down here in case anyone else finds it interesting.
Related to #12457Matthew PickeringMatthew Pickering