summaryrefslogtreecommitdiff
path: root/compiler/Language/Haskell/Syntax/Decls.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/Language/Haskell/Syntax/Decls.hs')
-rw-r--r--compiler/Language/Haskell/Syntax/Decls.hs85
1 files changed, 54 insertions, 31 deletions
diff --git a/compiler/Language/Haskell/Syntax/Decls.hs b/compiler/Language/Haskell/Syntax/Decls.hs
index baeef95b17..3ed3e5a4b0 100644
--- a/compiler/Language/Haskell/Syntax/Decls.hs
+++ b/compiler/Language/Haskell/Syntax/Decls.hs
@@ -73,7 +73,9 @@ module Language.Haskell.Syntax.Decls (
CImportSpec(..),
-- ** Data-constructor declarations
ConDecl(..), LConDecl,
- HsConDeclH98Details, HsConDeclGADTDetails(..),
+ HsConDeclH98Details,
+ ConGadtSigBody(..), PrefixConGadtSigBody(..),
+ anonPrefixConGadtSigArgs, prefixConGadtSigRes,
-- ** Document comments
DocDecl(..), LDocDecl, docDeclDoc,
-- ** Deprecations
@@ -1062,8 +1064,7 @@ data ConDecl pass
-- implicit. The 'XRec' is used to anchor exact print
-- annotations, AnnForall and AnnDot.
, con_mb_cxt :: Maybe (LHsContext pass) -- ^ User-written context (if any)
- , con_g_args :: HsConDeclGADTDetails pass -- ^ Arguments; never infix
- , con_res_ty :: LHsType pass -- ^ Result type
+ , con_body :: ConGadtSigBody pass -- ^ The argument and result types
, con_doc :: Maybe (LHsDoc pass) -- ^ A possible Haddock
-- comment.
@@ -1108,17 +1109,17 @@ There are two broad ways to classify GADT constructors:
data T a where
K :: forall a. Ord a => [a] -> ... -> T a
-This distinction is recorded in the `con_args :: HsConDetails pass`, which
-tracks if we're dealing with a RecCon or PrefixCon. It is easy to distinguish
-the two in the AST since record GADT constructors use HsRecTy. This distinction
-is made in GHC.Parser.PostProcess.mkGadtDecl.
+This distinction is recorded in the `con_body :: ConGadtSigBody pass` field,
+which tracks if we're dealing with a RecConGADT or PrefixConGADT. It is easy to
+distinguish the two in the AST since record GADT constructors use HsRecTy. This
+distinction is made in GHC.Parser.PostProcess.mkGadtDecl.
It is worth elaborating a bit more on the process of splitting the argument
types of a GADT constructor, since there are some non-obvious details involved.
While splitting the argument types of a record GADT constructor is easy (they
are stored in an HsRecTy), splitting the arguments of a prefix GADT constructor
is trickier. The basic idea is that we must split along the outermost function
-arrows ((->) and (%1 ->)) in the type, which GHC.Hs.Type.splitHsFunType
+arrows ((->) and (%1 ->)) in the type, which GHC.Hs.Type.splitLHsPrefixGadtSigBody
accomplishes. But what about type operators? Consider:
C :: a :*: b -> a :*: b -> a :+: b
@@ -1182,24 +1183,18 @@ or contexts in two parts:
1. GHC, in the process of splitting apart a GADT's type,
extracts out the leading `forall` and context (if they are provided). To
accomplish this splitting, the renamer uses the
- GHC.Hs.Type.splitLHsGADTPrefixTy function, which is careful not to remove
+ GHC.Hs.Type.splitLHsGadtTy function, which is careful not to remove
parentheses surrounding the leading `forall` or context (as these
parentheses can be syntactically significant). If the third result returned
- by splitLHsGADTPrefixTy contains any `forall`s or contexts, then they must
- be nested, so they will be rejected.
+ by splitLHsGadtTy contains any `forall`s or contexts, then they must
+ be nested, so they will be rejected later in the renamer.
Note that this step applies to both prefix and record GADTs alike, as they
- both have syntax which permits `forall`s and contexts. The difference is
- where this step happens:
-
- * For prefix GADTs, this happens in the renamer (in rnConDecl), as we cannot
- split until after the type operator fixities have been resolved.
- * For record GADTs, this happens in the parser (in mkGadtDecl).
-2. If the GADT type is prefix, the renamer (in the ConDeclGADTPrefixPs case of
- rnConDecl) will then check for nested `forall`s/contexts in the body of a
- prefix GADT type, after it has determined what all of the argument types are.
- This step is necessary to catch examples like MkT4 above, where the nested
- quantification occurs after a visible argument type.
+ both have syntax which permits `forall`s and contexts.
+2. The renamer (in GHC.Rename.Module.rnGADTResultTy) will then check for nested
+ `forall`s/contexts in the body of a GADT constructor type. This step is
+ necessary to catch examples like MkT4 above, where the nested quantification
+ occurs after a visible argument type.
-}
-- | The arguments in a Haskell98-style data constructor.
@@ -1208,15 +1203,43 @@ type HsConDeclH98Details pass
-- The Void argument to HsConDetails here is a reflection of the fact that
-- type applications are not allowed in data constructor declarations.
--- | The arguments in a GADT constructor. Unlike Haskell98-style constructors,
--- GADT constructors cannot be declared with infix syntax. As a result, we do
--- not use 'HsConDetails' here, as 'InfixCon' would be an unrepresentable
--- state. (There is a notion of infix GADT constructors for the purposes of
--- derived Show instances—see Note [Infix GADT constructors] in
--- GHC.Tc.TyCl—but that is an orthogonal concern.)
-data HsConDeclGADTDetails pass
- = PrefixConGADT [HsScaled pass (LBangType pass)]
- | RecConGADT (XRec pass [LConDeclField pass]) (LHsUniToken "->" "→" pass)
+-- | The argument and result types in a GADT constructor.
+-- See @Note [GADT abstract syntax]@.
+--
+-- Unlike Haskell98-style constructors, GADT constructors cannot be declared
+-- with infix syntax. As a result, we do not use 'HsConDetails' here, as
+-- 'InfixCon' would be an unrepresentable state. (There is a notion of infix
+-- GADT constructors for the purposes of derived 'Show' instances—see
+-- @Note [Infix GADT constructors]@ in "GHC.Tc.TyCl"—but that is an
+-- orthogonal concern.)
+data ConGadtSigBody pass
+ = PrefixConGADT (PrefixConGadtSigBody pass)
+ | RecConGADT (XRec pass [LConDeclField pass]) (LHsUniToken "->" "→" pass) (LHsType pass)
+
+-- | The argument and result types in a prefix GADT constructor. This closely
+-- resembles the structure of 'HsType', but with data constructor–specific
+-- tweaks. See @Note [GADT abstract syntax]@.
+data PrefixConGadtSigBody pass
+ -- | The result type.
+ = PCGSRes (LHsType pass)
+ -- | An argument followed by a function arrow (e.g., @MkT :: !Int -> ...@).
+ -- This is much like 'HsFunTy', except that 'PCGSAnonArg' uses an
+ -- 'LBangType' instead of an 'LHsType' to represent the argument.
+ | PCGSAnonArg (HsScaled pass (LBangType pass))
+ (PrefixConGadtSigBody pass)
+
+-- | Retrieve the visible, non-dependent arguments in a prefix GADT
+-- constructor type. Note that this takes O(/n/) time, where /n/ is the number
+-- of arguments.
+anonPrefixConGadtSigArgs :: PrefixConGadtSigBody pass -> [HsScaled pass (LBangType pass)]
+anonPrefixConGadtSigArgs (PCGSAnonArg arg body) = arg : anonPrefixConGadtSigArgs body
+anonPrefixConGadtSigArgs PCGSRes{} = []
+
+-- | Retrieve the result type in a prefix GADT constructor type. Note that this
+-- takes O(/n/) time, where /n/ is the number of arguments.
+prefixConGadtSigRes :: PrefixConGadtSigBody pass -> LHsType pass
+prefixConGadtSigRes (PCGSAnonArg _ body) = prefixConGadtSigRes body
+prefixConGadtSigRes (PCGSRes res_ty) = res_ty
instance Outputable NewOrData where
ppr NewType = text "newtype"