Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
GHC
GHC
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 4,312
    • Issues 4,312
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
    • Iterations
  • Merge Requests 385
    • Merge Requests 385
  • Requirements
    • Requirements
    • List
  • CI / CD
    • CI / CD
    • Pipelines
    • Jobs
    • Schedules
  • Security & Compliance
    • Security & Compliance
    • Dependency List
    • License Compliance
  • Operations
    • Operations
    • Incidents
    • Environments
  • Analytics
    • Analytics
    • CI / CD
    • Code Review
    • Insights
    • Issue
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Collapse sidebar
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • Glasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #15824

Closed
Open
Opened Oct 28, 2018 by Vladislav Zavialov@int-indexDeveloper

Prefix/infix distinction in TemplateHaskell types is lost

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: decomposeType from th-desugar.

This issue is in part responsible for #15815 (closed), see ticket:15815#comment:162853.

Edited Mar 10, 2019 by Vladislav Zavialov
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking
None
Due date
None
Reference: ghc/ghc#15824