[GHC] #15815: problem with splicing type into constraint
GHC
ghc-devs at haskell.org
Sun Oct 28 16:08:04 UTC 2018
#15815: problem with splicing type into constraint
-------------------------------------+-------------------------------------
Reporter: int-e | Owner: RyanGlScott
Type: bug | Status: new
Priority: highest | Milestone:
Component: Template Haskell | Version: 8.6.1
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: GHC rejects | Unknown/Multiple
valid program | Test Case:
Blocked By: | Blocking:
Related Tickets: #15760 | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Changes (by int-index):
* cc: int-index (added)
Comment:
Ryan, why do you believe that preserving parentheses would solve this
issue? The original code does not have any parentheses (unlike your
minimised version).
The issue here appears to be manyfold
1. We reassociate type operators according to their fixities, so with `a ~
$(tyQ)` and `[t| Int -> Int |]` from the original report we get `a ~ Int
-> Int` (no parens!) which gets correctly associated as `(a ~ Int) ->
Int`. That's not the behaviour I would expect, but I've just learned that
it's documented in `Note [Converting UInfix]`.
2. However, we map all three of `(~) a b`, `a ~ b`, and `(a ~ b)` to `AppT
(AppT EqualityT a) b`. So we discard both parenthesization information (as
you noted) and whether equality was applied in an infix or a prefix
fashion.
3. When we convert back to `HsType`, we use `HsOpTy` if there are two
operands, even if `(~)` was prefix in the original code. This means that
even if we fix #15760, we will still get incorrect behaviour in the `(~) a
b` case.
For expressions, we have a distinction between `UInfixE` and `InfixE`:
* `UInfixE` gets reassociated as necessary (documented in `See Note
[Operator association]`)
* `InfixE` gets parenthesised as necessary (documented in `Note
[Converting UInfix]`)
In types we have a similar distinction – `UInfixT` and `InfixT`. The
former must get reassociated, and the latter parenthesised.
NB. Looking at the code that handles `InfixT` it appears broken to me
(because unlike code for expressions, it does not call
`parenthesizeHsType`). But this is either a bug or I'm missing something.
For the sake of the argument let's assume `InfixT` is treated the same way
as `InfixE` – by parenthesising arguments as necessary.
The question is: do we treat `AppT (AppT (EqualityT a)) b` as a moral
equivalent of `UInfixT` or `InfixT`? The bug seems to be that we used to
treat it as `InfixT`, now we treat it as `UInfixT`. Your patch with adding
`parenthesizeHsType` seems to make it treated like `InfixT` again, but
ideally the fix should be
* to stop confusing `(~) a b` and `a ~ b`: map the former to `AppT (AppT
(EqualityT a)) b` and the latter to `InfixT a EqualityT b`
* to fix the treatment of `InfixT` in `cvtTypeKind` – parenthesise as
necessary in expressions
* drop this silly special case:
{{{
| [x',y'] <- tys' ->
returnL (HsOpTy noExt x' (noLoc eqTyCon_RDR) y')
}}}
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/15815#comment:5>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list