[GHC] #15824: Prefix/infix distinction in TemplateHaskell types is lost
GHC
ghc-devs at haskell.org
Sun Oct 28 20:08:09 UTC 2018
#15824: Prefix/infix distinction in TemplateHaskell types is lost
-------------------------------------+-------------------------------------
Reporter: int-index | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: Template Haskell | Version: 8.6.1
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: #15815, #15760 | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Description changed by int-index:
Old description:
> Consider `data T a b = a :. b`.
>
> In the declaration, `:.` is mapped to `InfixC`:
>
> {{{
> ghci> putStrLn $(reify ''T >>= stringE . show)
> TyConI (DataD [] T [KindedTV a StarT,KindedTV b StarT] Nothing [InfixC
> (Bang NoSourceUnpackedness NoSourceStrictness,VarT a) :. (Bang
> NoSourceUnpackedness NoSourceStrictness,VarT b)] [])
> }}}
>
> In expressions, `a :. b` is mapped to `InfixE`:
>
> {{{
> ghci> runQ [e| 1 :+ 2 |] >>= print
> InfixE (Just (LitE (IntegerL 1))) (ConE :+) (Just (LitE (IntegerL 2)))
> }}}
>
> In patterns, `a :. b` is mapped to `InfixP`:
>
> {{{
> ghci> runQ [p| 1 :. 2 |] >>= print
> InfixP (LitP (IntegerL 1)) :. (LitP (IntegerL 2))
> }}}
>
> In types, `a :. b` is mapped to `InfixT`:
>
> {{{
> ghci> runQ [t| 1 :. 2 |] >>= print
> InfixT (LitT (NumTyLit 1)) (PromotedT :.) (LitT (NumTyLit 2))
> }}}
>
> That last one was a lie. In reality, in types `a :. b` is mapped to
> nested `AppT`:
>
> {{{
> ghci> runQ [t| 1 :. 2 |] >>= print
> AppT (AppT (PromotedT :.) (LitT (NumTyLit 1))) (LitT (NumTyLit 2))
> }}}
>
> This is despite the existence of `InfixT`.
>
> The same issue can be observed when reifying types:
>
> {{{
> ghci> type A = 1 :. 2
> ghci> putStrLn $(reify ''A >>= stringE . show)
> TyConI (TySynD A [] (AppT (AppT (PromotedT :.) (LitT (NumTyLit 1))) (LitT
> (NumTyLit 2))))
> }}}
>
> This is not specific to infix constructors and can be observed with any
> infix (type) operators.
>
> It's best to change this in the same release as we fix #15760, as there
> is code in the wild that is prepared to face neither `InfixT` nor
> `ParensT`, and it would break silently. RyanGlScott gives an example of
> such code: [https://github.com/glguy/th-
> abstraction/blob/5123c6d054d0949cb444590269f13e5d44699ab2/src/Language/Haskell/TH/Datatype.hs#L1156-L1160
> decomposeType from th-desugar].
>
> This issue is in part responsible for #15815, see comment:5:ticket:15815.
New description:
Consider `data T a b = a :. b`.
In the declaration, `:.` is mapped to `InfixC`:
{{{
ghci> putStrLn $(reify ''T >>= stringE . show)
TyConI (DataD [] T [KindedTV a StarT,KindedTV b StarT] Nothing [InfixC
(Bang NoSourceUnpackedness NoSourceStrictness,VarT a) :. (Bang
NoSourceUnpackedness NoSourceStrictness,VarT b)] [])
}}}
In expressions, `a :. b` is mapped to `InfixE`:
{{{
ghci> runQ [e| 1 :+ 2 |] >>= print
InfixE (Just (LitE (IntegerL 1))) (ConE :+) (Just (LitE (IntegerL 2)))
}}}
In patterns, `a :. b` is mapped to `InfixP`:
{{{
ghci> runQ [p| 1 :. 2 |] >>= print
InfixP (LitP (IntegerL 1)) :. (LitP (IntegerL 2))
}}}
In types, `a :. b` is mapped to `InfixT`:
{{{
ghci> runQ [t| 1 :. 2 |] >>= print
InfixT (LitT (NumTyLit 1)) (PromotedT :.) (LitT (NumTyLit 2))
}}}
That last one was a lie. In reality, in types `a :. b` is mapped to nested
`AppT`, as if it was written as `(:.) a b`:
{{{
ghci> runQ [t| 1 :. 2 |] >>= print
AppT (AppT (PromotedT :.) (LitT (NumTyLit 1))) (LitT (NumTyLit 2))
}}}
This is despite the existence of `InfixT`.
The same issue can be observed when reifying types:
{{{
ghci> type A = 1 :. 2
ghci> putStrLn $(reify ''A >>= stringE . show)
TyConI (TySynD A [] (AppT (AppT (PromotedT :.) (LitT (NumTyLit 1))) (LitT
(NumTyLit 2))))
}}}
This is not specific to infix constructors and can be observed with any
infix (type) operators.
It's best to change this in the same release as we fix #15760, as there is
code in the wild that is prepared to face neither `InfixT` nor `ParensT`,
and it would break silently. RyanGlScott gives an example of such code:
[https://github.com/glguy/th-
abstraction/blob/5123c6d054d0949cb444590269f13e5d44699ab2/src/Language/Haskell/TH/Datatype.hs#L1156-L1160
decomposeType from th-desugar].
This issue is in part responsible for #15815, see comment:5:ticket:15815.
--
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/15824#comment:1>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list