Consider removing ConDeclGADTPrefixPs
The ConDecl
has two constructors, ConDeclH98
and ConDeclGADT
, as well as an extension constructor, XConDecl
. Currently, XConDecl
extends the parsing phase with a GHC-specific data type called ConDeclGADTPrefix
:
data ConDeclGADTPrefixPs = ConDeclGADTPrefixPs
{ con_gp_names :: [Located RdrName]
-- ^ The GADT constructor declaration's names.
, con_gp_ty :: LHsSigType GhcPs
-- ^ The type after the @::@.
, con_gp_doc :: Maybe LHsDocString
-- ^ A possible Haddock comment.
}
This is used for prefix GADT constructors (e.g., MkT1 :: a -> T a
), as opposed to record GADT constructors (e.g., MkT2 :: { unT2 :: a } -> T a
).
Why do we have both ConDeclGADT
and ConDeclGADTPrefixPs
? Note [GADT abstract syntax]
explains the complications of parsing prefix GADT constructors. Here is the relevant excerpt:
For prefix GADT constructors, however, the situation is more complicated. It
can be difficult to split a prefix GADT type until we know type operator
fixities. Consider this, for example:
C :: a :*: b -> a :*: b -> a :+: b
Initially, the type of C will parse as:
a :*: (b -> (a :*: (b -> (a :+: b))))
So it's hard to split up the arguments until we've done the precedence
resolution (in the renamer). (Unlike prefix GADT types, record GADT types
do not have this problem because of their uniform syntax.)
As a result, we deliberately avoid splitting prefix GADT types in the parser.
Instead, we store the entire LHsType in ConDeclGADTPrefixPs, a GHC-specific
extension constructor to ConDecl. Later, in the renamer
(in GHC.Rename.Module.rnConDecl), we resolve the fixities of all type operators
in the LHsType, which facilitates splitting it into argument and result types
accurately. We finish renaming a ConDeclGADTPrefixPs by putting the split
components into a ConDeclGADT. This is why ConDeclGADTPrefixPs has the suffix
-Ps, as it is only used by the parser.
However, @int-index pointed out to me in !3337 (comment 282965) that the premise behind this Note is incorrect. GHC always parses function arrows more loosely than any user-definable type operator due to its special treatment in the parser, so a :*: b -> a :*: b -> a :+: b
will actually be parsed as (a :*: b) -> ((a :*: b) -> (a :+: b))
, not a :*: (b -> (a :*: (b -> (a :+: b))))
(as the Note claims). As a consequence, it should be simple to split up a prefix GADT constructor type in the parser, which obviates the need for ConDeclGADTPrefixPs
in the first place.
I'm opening this issue to track removing ConDeclGADTPrefixPs
in light of the discussion above. I will post a MR soon, but if anyone can think of any looming issues that I've overlooked, do speak up.