[GHC] #15824: Prefix/infix distinction in TemplateHaskell types is lost

#15824: Prefix/infix distinction in TemplateHaskell types is lost -------------------------------------+------------------------------------- Reporter: int-index | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Template | Version: 8.6.1 Haskell | Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: #15815, #15760 Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- 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. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15824 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#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

#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: | -------------------------------------+------------------------------------- Changes (by int-e): * cc: int-e (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15824#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC