diff options
author | RyanGlScott <ryan.gl.scott@gmail.com> | 2015-12-22 11:25:59 +0100 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2015-12-22 13:22:29 +0100 |
commit | f975b0b10b2971d00b6e1986e0a2af2bf759a4f4 (patch) | |
tree | 8b890f6e8058bb0a625a409de70f107101048d8d /libraries/template-haskell | |
parent | b407bd775d9241023b4694b3142a756df0082ea2 (diff) | |
download | haskell-f975b0b10b2971d00b6e1986e0a2af2bf759a4f4.tar.gz |
Rework Template Haskell's handling of strictness
Currently, Template Haskell's treatment of strictness is not enough to
cover all possible combinations of unpackedness and strictness. In
addition, it isn't equipped to deal with new features (such as
`-XStrictData`) which can change a datatype's fields' strictness during
compilation.
To address this, I replaced TH's `Strict` datatype with
`SourceUnpackedness` and `SourceStrictness` (which give the programmer a
more complete toolkit to configure a datatype field's strictness than
just `IsStrict`, `IsLazy`, and `Unpack`). I also added the ability to
reify a constructor fields' strictness post-compilation through the
`reifyConStrictness` function.
Fixes #10697.
Test Plan: ./validate
Reviewers: simonpj, goldfire, bgamari, austin
Reviewed By: goldfire, bgamari
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D1603
GHC Trac Issues: #10697
Diffstat (limited to 'libraries/template-haskell')
-rw-r--r-- | libraries/template-haskell/Language/Haskell/TH.hs | 16 | ||||
-rw-r--r-- | libraries/template-haskell/Language/Haskell/TH/Lib.hs | 97 | ||||
-rw-r--r-- | libraries/template-haskell/Language/Haskell/TH/Ppr.hs | 66 | ||||
-rw-r--r-- | libraries/template-haskell/Language/Haskell/TH/Syntax.hs | 162 | ||||
-rw-r--r-- | libraries/template-haskell/changelog.md | 12 |
5 files changed, 245 insertions, 108 deletions
diff --git a/libraries/template-haskell/Language/Haskell/TH.hs b/libraries/template-haskell/Language/Haskell/TH.hs index 66d507cf9d..19882868b0 100644 --- a/libraries/template-haskell/Language/Haskell/TH.hs +++ b/libraries/template-haskell/Language/Haskell/TH.hs @@ -41,6 +41,8 @@ module Language.Haskell.TH( reifyRoles, -- *** Annotation lookup reifyAnnotations, AnnLookup(..), + -- *** Constructor strictness lookup + reifyConStrictness, -- * Typed expressions TExp, unType, @@ -66,7 +68,8 @@ module Language.Haskell.TH( -- ** Declarations Dec(..), Con(..), Clause(..), - Strict(..), Foreign(..), Callconv(..), Safety(..), Pragma(..), + SourceUnpackedness(..), SourceStrictness(..), DecidedStrictness(..), + Bang(..), Strict, Foreign(..), Callconv(..), Safety(..), Pragma(..), Inline(..), RuleMatch(..), Phases(..), RuleBndr(..), AnnTarget(..), FunDep(..), FamFlavour(..), TySynEqn(..), TypeFamilyHead(..), Fixity(..), FixityDirection(..), defaultFixity, maxPrecedence, @@ -80,9 +83,10 @@ module Language.Haskell.TH( -- * Library functions -- ** Abbreviations - InfoQ, ExpQ, DecQ, DecsQ, ConQ, TypeQ, TyLitQ, CxtQ, PredQ, MatchQ, ClauseQ, - BodyQ, GuardQ, StmtQ, RangeQ, StrictTypeQ, VarStrictTypeQ, PatQ, FieldPatQ, - RuleBndrQ, TySynEqnQ, + InfoQ, ExpQ, DecQ, DecsQ, ConQ, TypeQ, TyLitQ, CxtQ, PredQ, MatchQ, + ClauseQ, BodyQ, GuardQ, StmtQ, RangeQ, SourceStrictnessQ, + SourceUnpackednessQ, BangTypeQ, VarBangTypeQ, StrictTypeQ, + VarStrictTypeQ, PatQ, FieldPatQ, RuleBndrQ, TySynEqnQ, -- ** Constructors lifted to 'Q' -- *** Literals @@ -119,7 +123,9 @@ module Language.Haskell.TH( -- **** Type literals numTyLit, strTyLit, -- **** Strictness - isStrict, notStrict, strictType, varStrictType, + noSourceUnpackedness, sourceNoUnpack, sourceUnpack, + noSourceStrictness, sourceLazy, sourceStrict, + bang, bangType, varBangType, strictType, varStrictType, -- **** Class Contexts cxt, classP, equalP, -- **** Constructors diff --git a/libraries/template-haskell/Language/Haskell/TH/Lib.hs b/libraries/template-haskell/Language/Haskell/TH/Lib.hs index 737b9d42c7..ef928e8a36 100644 --- a/libraries/template-haskell/Language/Haskell/TH/Lib.hs +++ b/libraries/template-haskell/Language/Haskell/TH/Lib.hs @@ -18,31 +18,38 @@ import Data.Word( Word8 ) -- * Type synonyms ---------------------------------------------------------- -type InfoQ = Q Info -type PatQ = Q Pat -type FieldPatQ = Q FieldPat -type ExpQ = Q Exp -type TExpQ a = Q (TExp a) -type DecQ = Q Dec -type DecsQ = Q [Dec] -type ConQ = Q Con -type TypeQ = Q Type -type TyLitQ = Q TyLit -type CxtQ = Q Cxt -type PredQ = Q Pred -type MatchQ = Q Match -type ClauseQ = Q Clause -type BodyQ = Q Body -type GuardQ = Q Guard -type StmtQ = Q Stmt -type RangeQ = Q Range -type StrictTypeQ = Q StrictType -type VarStrictTypeQ = Q VarStrictType -type FieldExpQ = Q FieldExp -type RuleBndrQ = Q RuleBndr -type TySynEqnQ = Q TySynEqn -type Role = TH.Role -- must be defined here for DsMeta to find it -type InjectivityAnn = TH.InjectivityAnn +type InfoQ = Q Info +type PatQ = Q Pat +type FieldPatQ = Q FieldPat +type ExpQ = Q Exp +type TExpQ a = Q (TExp a) +type DecQ = Q Dec +type DecsQ = Q [Dec] +type ConQ = Q Con +type TypeQ = Q Type +type TyLitQ = Q TyLit +type CxtQ = Q Cxt +type PredQ = Q Pred +type MatchQ = Q Match +type ClauseQ = Q Clause +type BodyQ = Q Body +type GuardQ = Q Guard +type StmtQ = Q Stmt +type RangeQ = Q Range +type SourceStrictnessQ = Q SourceStrictness +type SourceUnpackednessQ = Q SourceUnpackedness +type BangQ = Q Bang +type BangTypeQ = Q BangType +type VarBangTypeQ = Q VarBangType +type StrictTypeQ = Q StrictType +type VarStrictTypeQ = Q VarStrictType +type FieldExpQ = Q FieldExp +type RuleBndrQ = Q RuleBndr +type TySynEqnQ = Q TySynEqn + +-- must be defined here for DsMeta to find it +type Role = TH.Role +type InjectivityAnn = TH.InjectivityAnn ---------------------------------------------------------- -- * Lowercase pattern syntax functions @@ -529,13 +536,13 @@ tySynEqn lhs rhs = cxt :: [PredQ] -> CxtQ cxt = sequence -normalC :: Name -> [StrictTypeQ] -> ConQ +normalC :: Name -> [BangTypeQ] -> ConQ normalC con strtys = liftM (NormalC con) $ sequence strtys -recC :: Name -> [VarStrictTypeQ] -> ConQ +recC :: Name -> [VarBangTypeQ] -> ConQ recC con varstrtys = liftM (RecC con) $ sequence varstrtys -infixC :: Q (Strict, Type) -> Name -> Q (Strict, Type) -> ConQ +infixC :: Q (Bang, Type) -> Name -> Q (Bang, Type) -> ConQ infixC st1 con st2 = do st1' <- st1 st2' <- st2 return $ InfixC st1' con st2' @@ -644,17 +651,37 @@ promotedNilT = return PromotedNilT promotedConsT :: TypeQ promotedConsT = return PromotedConsT -isStrict, notStrict, unpacked :: Q Strict -isStrict = return $ IsStrict -notStrict = return $ NotStrict -unpacked = return Unpacked +noSourceUnpackedness, sourceNoUnpack, sourceUnpack :: SourceUnpackednessQ +noSourceUnpackedness = return NoSourceUnpackedness +sourceNoUnpack = return SourceNoUnpack +sourceUnpack = return SourceUnpack +noSourceStrictness, sourceLazy, sourceStrict :: SourceStrictnessQ +noSourceStrictness = return NoSourceStrictness +sourceLazy = return SourceLazy +sourceStrict = return SourceStrict + +bang :: SourceUnpackednessQ -> SourceStrictnessQ -> BangQ +bang u s = do u' <- u + s' <- s + return (Bang u' s') + +bangType :: BangQ -> TypeQ -> BangTypeQ +bangType = liftM2 (,) + +varBangType :: Name -> BangTypeQ -> VarBangTypeQ +varBangType v bt = do (b, t) <- bt + return (v, b, t) + +{-# DEPRECATED strictType + "As of @template-haskell-2.11.0.0@, 'StrictType' has been replaced by 'BangType'. Please use 'bangType' instead." #-} strictType :: Q Strict -> TypeQ -> StrictTypeQ -strictType = liftM2 (,) +strictType = bangType +{-# DEPRECATED varStrictType + "As of @template-haskell-2.11.0.0@, 'VarStrictType' has been replaced by 'VarBangType'. Please use 'varBangType' instead." #-} varStrictType :: Name -> StrictTypeQ -> VarStrictTypeQ -varStrictType v st = do (s, t) <- st - return (v, s, t) +varStrictType = varBangType -- * Type Literals diff --git a/libraries/template-haskell/Language/Haskell/TH/Ppr.hs b/libraries/template-haskell/Language/Haskell/TH/Ppr.hs index bf240f4ec5..d02ad0a30a 100644 --- a/libraries/template-haskell/Language/Haskell/TH/Ppr.hs +++ b/libraries/template-haskell/Language/Haskell/TH/Ppr.hs @@ -497,14 +497,14 @@ instance Ppr Clause where ------------------------------ instance Ppr Con where - ppr (NormalC c sts) = ppr c <+> sep (map pprStrictType sts) + ppr (NormalC c sts) = ppr c <+> sep (map pprBangType sts) ppr (RecC c vsts) - = ppr c <+> braces (sep (punctuate comma $ map pprVarStrictType vsts)) + = ppr c <+> braces (sep (punctuate comma $ map pprVarBangType vsts)) - ppr (InfixC st1 c st2) = pprStrictType st1 + ppr (InfixC st1 c st2) = pprBangType st1 <+> pprName' Infix c - <+> pprStrictType st2 + <+> pprBangType st2 ppr (ForallC ns ctxt (GadtC c sts ty idx)) = commaSep c <+> dcolon <+> pprForall ns ctxt <+> pprGadtRHS sts ty idx @@ -529,27 +529,69 @@ pprForall ns ctxt pprRecFields :: [(Name, Strict, Type)] -> Name -> [Type] -> Doc pprRecFields vsts ty idx - = braces (sep (punctuate comma $ map pprVarStrictType vsts)) + = braces (sep (punctuate comma $ map pprVarBangType vsts)) <+> arrow <+> ppr ty <+> sep (map ppr idx) pprGadtRHS :: [(Strict, Type)] -> Name -> [Type] -> Doc pprGadtRHS [] ty idx = ppr ty <+> sep (map ppr idx) pprGadtRHS sts ty idx - = sep (punctuate (space <> arrow) (map pprStrictType sts)) + = sep (punctuate (space <> arrow) (map pprBangType sts)) <+> arrow <+> ppr ty <+> sep (map ppr idx) ------------------------------ -pprVarStrictType :: (Name, Strict, Type) -> Doc +pprVarBangType :: VarBangType -> Doc -- Slight infelicity: with print non-atomic type with parens -pprVarStrictType (v, str, t) = ppr v <+> dcolon <+> pprStrictType (str, t) +pprVarBangType (v, bang, t) = ppr v <+> dcolon <+> pprBangType (bang, t) + +------------------------------ +pprBangType :: BangType -> Doc +-- Make sure we print +-- +-- Con {-# UNPACK #-} a +-- +-- rather than +-- +-- Con {-# UNPACK #-}a +-- +-- when there's no strictness annotation. If there is a strictness annotation, +-- it's okay to not put a space between it and the type. +pprBangType (bt@(Bang _ NoSourceStrictness), t) = ppr bt <+> pprParendType t +pprBangType (bt, t) = ppr bt <> pprParendType t + +------------------------------ +instance Ppr Bang where + ppr (Bang su ss) = ppr su <+> ppr ss + +------------------------------ +instance Ppr SourceUnpackedness where + ppr NoSourceUnpackedness = empty + ppr SourceNoUnpack = text "{-# NOUNPACK #-}" + ppr SourceUnpack = text "{-# UNPACK #-}" + +------------------------------ +instance Ppr SourceStrictness where + ppr NoSourceStrictness = empty + ppr SourceLazy = char '~' + ppr SourceStrict = char '!' + +------------------------------ +instance Ppr DecidedStrictness where + ppr DecidedLazy = empty + ppr DecidedStrict = char '!' + ppr DecidedUnpack = text "{-# UNPACK #-} !" + +------------------------------ +{-# DEPRECATED pprVarStrictType + "As of @template-haskell-2.11.0.0@, 'VarStrictType' has been replaced by 'VarBangType'. Please use 'pprVarBangType' instead." #-} +pprVarStrictType :: (Name, Strict, Type) -> Doc +pprVarStrictType = pprVarBangType ------------------------------ +{-# DEPRECATED pprStrictType + "As of @template-haskell-2.11.0.0@, 'StrictType' has been replaced by 'BangType'. Please use 'pprBangType' instead." #-} pprStrictType :: (Strict, Type) -> Doc --- Prints with parens if not already atomic -pprStrictType (IsStrict, t) = char '!' <> pprParendType t -pprStrictType (NotStrict, t) = pprParendType t -pprStrictType (Unpacked, t) = text "{-# UNPACK #-} !" <> pprParendType t +pprStrictType = pprBangType ------------------------------ pprParendType :: Type -> Doc diff --git a/libraries/template-haskell/Language/Haskell/TH/Syntax.hs b/libraries/template-haskell/Language/Haskell/TH/Syntax.hs index b333b006b6..d10fb3c0a5 100644 --- a/libraries/template-haskell/Language/Haskell/TH/Syntax.hs +++ b/libraries/template-haskell/Language/Haskell/TH/Syntax.hs @@ -76,9 +76,10 @@ class (Applicative m, Monad m) => Quasi m where -- Returns list of matching instance Decs -- (with empty sub-Decs) -- Works for classes and type functions - qReifyRoles :: Name -> m [Role] - qReifyAnnotations :: Data a => AnnLookup -> m [a] - qReifyModule :: Module -> m ModuleInfo + qReifyRoles :: Name -> m [Role] + qReifyAnnotations :: Data a => AnnLookup -> m [a] + qReifyModule :: Module -> m ModuleInfo + qReifyConStrictness :: Name -> m [DecidedStrictness] qLocation :: m Loc @@ -117,22 +118,23 @@ instance Quasi IO where qReport True msg = hPutStrLn stderr ("Template Haskell error: " ++ msg) qReport False msg = hPutStrLn stderr ("Template Haskell error: " ++ msg) - qLookupName _ _ = badIO "lookupName" - qReify _ = badIO "reify" - qReifyFixity _ = badIO "reifyFixity" - qReifyInstances _ _ = badIO "reifyInstances" - qReifyRoles _ = badIO "reifyRoles" - qReifyAnnotations _ = badIO "reifyAnnotations" - qReifyModule _ = badIO "reifyModule" - qLocation = badIO "currentLocation" - qRecover _ _ = badIO "recover" -- Maybe we could fix this? - qAddDependentFile _ = badIO "addDependentFile" - qAddTopDecls _ = badIO "addTopDecls" - qAddModFinalizer _ = badIO "addModFinalizer" - qGetQ = badIO "getQ" - qPutQ _ = badIO "putQ" - qIsExtEnabled _ = badIO "isExtEnabled" - qExtsEnabled = badIO "extsEnabled" + qLookupName _ _ = badIO "lookupName" + qReify _ = badIO "reify" + qReifyFixity _ = badIO "reifyFixity" + qReifyInstances _ _ = badIO "reifyInstances" + qReifyRoles _ = badIO "reifyRoles" + qReifyAnnotations _ = badIO "reifyAnnotations" + qReifyModule _ = badIO "reifyModule" + qReifyConStrictness _ = badIO "reifyConStrictness" + qLocation = badIO "currentLocation" + qRecover _ _ = badIO "recover" -- Maybe we could fix this? + qAddDependentFile _ = badIO "addDependentFile" + qAddTopDecls _ = badIO "addTopDecls" + qAddModFinalizer _ = badIO "addModFinalizer" + qGetQ = badIO "getQ" + qPutQ _ = badIO "putQ" + qIsExtEnabled _ = badIO "isExtEnabled" + qExtsEnabled = badIO "extsEnabled" qRunIO m = m @@ -391,6 +393,21 @@ reifyAnnotations an = Q (qReifyAnnotations an) reifyModule :: Module -> Q ModuleInfo reifyModule m = Q (qReifyModule m) +-- | @reifyConStrictness nm@ looks up the strictness information for the fields +-- of the constructor with the name @nm@. Note that the strictness information +-- that 'reifyConStrictness' returns may not correspond to what is written in +-- the source code. For example, in the following data declaration: +-- +-- @ +-- data Pair a = Pair a a +-- @ +-- +-- 'reifyConStrictness' would return @['DecidedLazy', DecidedLazy]@ under most +-- circumstances, but it would return @['DecidedStrict', DecidedStrict]@ if the +-- @-XStrictData@ language extension was enabled. +reifyConStrictness :: Name -> Q [DecidedStrictness] +reifyConStrictness n = Q (qReifyConStrictness n) + -- | Is the list of instances returned by 'reifyInstances' nonempty? isInstance :: Name -> [Type] -> Q Bool isInstance nm tys = do { decs <- reifyInstances nm tys @@ -451,25 +468,26 @@ extsEnabled :: Q [Extension] extsEnabled = Q qExtsEnabled instance Quasi Q where - qNewName = newName - qReport = report - qRecover = recover - qReify = reify - qReifyFixity = reifyFixity - qReifyInstances = reifyInstances - qReifyRoles = reifyRoles - qReifyAnnotations = reifyAnnotations - qReifyModule = reifyModule - qLookupName = lookupName - qLocation = location - qRunIO = runIO - qAddDependentFile = addDependentFile - qAddTopDecls = addTopDecls - qAddModFinalizer = addModFinalizer - qGetQ = getQ - qPutQ = putQ - qIsExtEnabled = isExtEnabled - qExtsEnabled = extsEnabled + qNewName = newName + qReport = report + qRecover = recover + qReify = reify + qReifyFixity = reifyFixity + qReifyInstances = reifyInstances + qReifyRoles = reifyRoles + qReifyAnnotations = reifyAnnotations + qReifyModule = reifyModule + qReifyConStrictness = reifyConStrictness + qLookupName = lookupName + qLocation = location + qRunIO = runIO + qAddDependentFile = addDependentFile + qAddTopDecls = addTopDecls + qAddModFinalizer = addModFinalizer + qGetQ = getQ + qPutQ = putQ + qIsExtEnabled = isExtEnabled + qExtsEnabled = extsEnabled ---------------------------------------------------- @@ -1593,22 +1611,39 @@ type Cxt = [Pred] -- ^ @(Eq a, Ord b)@ -- be tuples of other constraints. type Pred = Type -data Strict = IsStrict | NotStrict | Unpacked - deriving( Show, Eq, Ord, Data, Typeable, Generic ) - -data Con = NormalC Name [StrictType] -- ^ @C Int a@ - | RecC Name [VarStrictType] -- ^ @C { v :: Int, w :: a }@ - | InfixC StrictType Name StrictType -- ^ @Int :+ a@ - | ForallC [TyVarBndr] Cxt Con -- ^ @forall a. Eq a => C [a]@ - | GadtC [Name] [StrictType] - Name -- See Note [GADT return type] - [Type] -- Indices of the type constructor - -- ^ @C :: a -> b -> T b Int@ - | RecGadtC [Name] [VarStrictType] - Name -- See Note [GADT return type] - [Type] -- Indices of the type constructor - -- ^ @C :: { v :: Int } -> T b Int@ - deriving( Show, Eq, Ord, Data, Typeable, Generic ) +data SourceUnpackedness + = NoSourceUnpackedness -- ^ @C a@ + | SourceNoUnpack -- ^ @C { {\-\# NOUNPACK \#-\} } a@ + | SourceUnpack -- ^ @C { {\-\# UNPACK \#-\} } a@ + deriving (Show, Eq, Ord, Data, Typeable, Generic) + +data SourceStrictness = NoSourceStrictness -- ^ @C a@ + | SourceLazy -- ^ @C {~}a@ + | SourceStrict -- ^ @C {!}a@ + deriving (Show, Eq, Ord, Data, Typeable, Generic) + +-- | Unlike 'SourceStrictness' and 'SourceUnpackedness', 'DecidedStrictness' +-- refers to the strictness that the compiler chooses for a data constructor +-- field, which may be different from what is written in source code. See +-- 'reifyConStrictness' for more information. +data DecidedStrictness = DecidedLazy + | DecidedStrict + | DecidedUnpack + deriving (Show, Eq, Ord, Data, Typeable, Generic) + +data Con = NormalC Name [BangType] -- ^ @C Int a@ + | RecC Name [VarBangType] -- ^ @C { v :: Int, w :: a }@ + | InfixC BangType Name BangType -- ^ @Int :+ a@ + | ForallC [TyVarBndr] Cxt Con -- ^ @forall a. Eq a => C [a]@ + | GadtC [Name] [BangType] + Name -- See Note [GADT return type] + [Type] -- Indices of the type constructor + -- ^ @C :: a -> b -> T b Int@ + | RecGadtC [Name] [VarBangType] + Name -- See Note [GADT return type] + [Type] -- Indices of the type constructor + -- ^ @C :: { v :: Int } -> T b Int@ + deriving (Show, Eq, Ord, Data, Typeable, Generic) -- Note [GADT return type] -- ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1621,8 +1656,23 @@ data Con = NormalC Name [StrictType] -- ^ @C Int a@ -- data T a where -- MkT :: S Int -type StrictType = (Strict, Type) -type VarStrictType = (Name, Strict, Type) +data Bang = Bang SourceUnpackedness SourceStrictness + -- ^ @C { {\-\# UNPACK \#-\} !}a@ + deriving (Show, Eq, Ord, Data, Typeable, Generic) + +type BangType = (Bang, Type) +type VarBangType = (Name, Bang, Type) + +-- | As of @template-haskell-2.11.0.0@, 'Strict' has been replaced by 'Bang'. +type Strict = Bang + +-- | As of @template-haskell-2.11.0.0@, 'StrictType' has been replaced by +-- 'BangType'. +type StrictType = BangType + +-- | As of @template-haskell-2.11.0.0@, 'VarStrictType' has been replaced by +-- 'VarBangType'. +type VarStrictType = VarBangType data Type = ForallT [TyVarBndr] Cxt Type -- ^ @forall \<vars\>. \<ctxt\> -> \<type\>@ | AppT Type Type -- ^ @T a b@ diff --git a/libraries/template-haskell/changelog.md b/libraries/template-haskell/changelog.md index 33419b34ec..9564e95678 100644 --- a/libraries/template-haskell/changelog.md +++ b/libraries/template-haskell/changelog.md @@ -25,6 +25,18 @@ * Add `TypeFamilyHead` for common elements of `OpenTypeFamilyD` and `ClosedTypeFamilyD` (#10902) + * The `Strict` datatype was split among different datatypes: three for + writing the strictness information of data constructors' fields as denoted + in Haskell source code (`SourceUnpackedness` and `SourceStrictness`, as + well as `Bang`), and one for strictness information after a constructor is + compiled (`DecidedStrictness`). `Strict`, `StrictType` and `VarStrictType` + have been deprecated in favor of `Bang`, `BangType` and `VarBangType`, and + three functions (`isStrict`, `isLazy`, and `unpack`) were removed because + they no longer serve any use in this new design. (#10697) + + * Add `reifyConStrictness` to query a data constructor's `DecidedStrictness` + values for its fields (#10697) + * TODO: document API changes and important bugfixes |