summaryrefslogtreecommitdiff
path: root/compiler/typecheck/TcDerivUtils.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/typecheck/TcDerivUtils.hs')
-rw-r--r--compiler/typecheck/TcDerivUtils.hs489
1 files changed, 363 insertions, 126 deletions
diff --git a/compiler/typecheck/TcDerivUtils.hs b/compiler/typecheck/TcDerivUtils.hs
index 09876afb70..86205de5fd 100644
--- a/compiler/typecheck/TcDerivUtils.hs
+++ b/compiler/typecheck/TcDerivUtils.hs
@@ -6,22 +6,25 @@
Error-checking and other utilities for @deriving@ clauses or declarations.
-}
-{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE TypeFamilies #-}
module TcDerivUtils (
+ DerivM, DerivEnv(..),
DerivSpec(..), pprDerivSpec,
- DerivSpecMechanism(..), isDerivSpecStock,
- isDerivSpecNewtype, isDerivSpecAnyClass,
- DerivContext, DerivStatus(..),
+ DerivSpecMechanism(..), derivSpecMechanismToStrategy, isDerivSpecStock,
+ isDerivSpecNewtype, isDerivSpecAnyClass, isDerivSpecVia,
+ DerivContext(..), OriginativeDerivStatus(..),
+ isStandaloneDeriv, isStandaloneWildcardDeriv, mkDerivOrigin,
PredOrigin(..), ThetaOrigin(..), mkPredOrigin,
mkThetaOrigin, mkThetaOriginFromPreds, substPredOrigin,
- checkSideConditions, hasStockDeriving,
+ checkOriginativeSideConditions, hasStockDeriving,
canDeriveAnyClass,
std_class_via_coercible, non_coercible_class,
newDerivClsInst, extendLocalInstEnv
) where
+import GhcPrelude
+
import Bag
import BasicTypes
import Class
@@ -49,18 +52,109 @@ import Type
import Util
import VarSet
+import Control.Monad.Trans.Reader
+import Data.Maybe
import qualified GHC.LanguageExtensions as LangExt
import ListSetOps (assocMaybe)
-data DerivSpec theta = DS { ds_loc :: SrcSpan
- , ds_name :: Name -- DFun name
- , ds_tvs :: [TyVar]
- , ds_theta :: theta
- , ds_cls :: Class
- , ds_tys :: [Type]
- , ds_tc :: TyCon
- , ds_overlap :: Maybe OverlapMode
- , ds_mechanism :: DerivSpecMechanism }
+-- | To avoid having to manually plumb everything in 'DerivEnv' throughout
+-- various functions in @TcDeriv@ and @TcDerivInfer@, we use 'DerivM', which
+-- is a simple reader around 'TcRn'.
+type DerivM = ReaderT DerivEnv TcRn
+
+-- | Is GHC processing a stanalone deriving declaration?
+isStandaloneDeriv :: DerivM Bool
+isStandaloneDeriv = asks (go . denv_ctxt)
+ where
+ go :: DerivContext -> Bool
+ go (InferContext wildcard) = isJust wildcard
+ go (SupplyContext {}) = True
+
+-- | Is GHC processing a standalone deriving declaration with an
+-- extra-constraints wildcard as the context?
+-- (e.g., @deriving instance _ => Eq (Foo a)@)
+isStandaloneWildcardDeriv :: DerivM Bool
+isStandaloneWildcardDeriv = asks (go . denv_ctxt)
+ where
+ go :: DerivContext -> Bool
+ go (InferContext wildcard) = isJust wildcard
+ go (SupplyContext {}) = False
+
+-- | @'mkDerivOrigin' wc@ returns 'StandAloneDerivOrigin' if @wc@ is 'True',
+-- and 'DerivClauseOrigin' if @wc@ is 'False'. Useful for error-reporting.
+mkDerivOrigin :: Bool -> CtOrigin
+mkDerivOrigin standalone_wildcard
+ | standalone_wildcard = StandAloneDerivOrigin
+ | otherwise = DerivClauseOrigin
+
+-- | Contains all of the information known about a derived instance when
+-- determining what its @EarlyDerivSpec@ should be.
+data DerivEnv = DerivEnv
+ { denv_overlap_mode :: Maybe OverlapMode
+ -- ^ Is this an overlapping instance?
+ , denv_tvs :: [TyVar]
+ -- ^ Universally quantified type variables in the instance
+ , denv_cls :: Class
+ -- ^ Class for which we need to derive an instance
+ , denv_cls_tys :: [Type]
+ -- ^ Other arguments to the class except the last
+ , denv_tc :: TyCon
+ -- ^ Type constructor for which the instance is requested
+ -- (last arguments to the type class)
+ , denv_tc_args :: [Type]
+ -- ^ Arguments to the type constructor
+ , denv_rep_tc :: TyCon
+ -- ^ The representation tycon for 'denv_tc'
+ -- (for data family instances)
+ , denv_rep_tc_args :: [Type]
+ -- ^ The representation types for 'denv_tc_args'
+ -- (for data family instances)
+ , denv_ctxt :: DerivContext
+ -- ^ @'SupplyContext' theta@ for standalone deriving (where @theta@ is the
+ -- context of the instance).
+ -- 'InferContext' for @deriving@ clauses, or for standalone deriving that
+ -- uses a wildcard constraint.
+ -- See @Note [Inferring the instance context]@.
+ , denv_strat :: Maybe (DerivStrategy GhcTc)
+ -- ^ 'Just' if user requests a particular deriving strategy.
+ -- Otherwise, 'Nothing'.
+ }
+
+instance Outputable DerivEnv where
+ ppr (DerivEnv { denv_overlap_mode = overlap_mode
+ , denv_tvs = tvs
+ , denv_cls = cls
+ , denv_cls_tys = cls_tys
+ , denv_tc = tc
+ , denv_tc_args = tc_args
+ , denv_rep_tc = rep_tc
+ , denv_rep_tc_args = rep_tc_args
+ , denv_ctxt = ctxt
+ , denv_strat = mb_strat })
+ = hang (text "DerivEnv")
+ 2 (vcat [ text "denv_overlap_mode" <+> ppr overlap_mode
+ , text "denv_tvs" <+> ppr tvs
+ , text "denv_cls" <+> ppr cls
+ , text "denv_cls_tys" <+> ppr cls_tys
+ , text "denv_tc" <+> ppr tc
+ , text "denv_tc_args" <+> ppr tc_args
+ , text "denv_rep_tc" <+> ppr rep_tc
+ , text "denv_rep_tc_args" <+> ppr rep_tc_args
+ , text "denv_ctxt" <+> ppr ctxt
+ , text "denv_strat" <+> ppr mb_strat ])
+
+data DerivSpec theta = DS { ds_loc :: SrcSpan
+ , ds_name :: Name -- DFun name
+ , ds_tvs :: [TyVar]
+ , ds_theta :: theta
+ , ds_cls :: Class
+ , ds_tys :: [Type]
+ , ds_tc :: TyCon
+ , ds_overlap :: Maybe OverlapMode
+ , ds_standalone_wildcard :: Maybe SrcSpan
+ -- See Note [Inferring the instance context]
+ -- in TcDerivInfer
+ , ds_mechanism :: DerivSpecMechanism }
-- This spec implies a dfun declaration of the form
-- df :: forall tvs. theta => C tys
-- The Name is the name for the DFun we'll build
@@ -90,15 +184,17 @@ Example:
pprDerivSpec :: Outputable theta => DerivSpec theta -> SDoc
pprDerivSpec (DS { ds_loc = l, ds_name = n, ds_tvs = tvs, ds_cls = c,
- ds_tys = tys, ds_theta = rhs, ds_mechanism = mech })
+ ds_tys = tys, ds_theta = rhs,
+ ds_standalone_wildcard = wildcard, ds_mechanism = mech })
= hang (text "DerivSpec")
- 2 (vcat [ text "ds_loc =" <+> ppr l
- , text "ds_name =" <+> ppr n
- , text "ds_tvs =" <+> ppr tvs
- , text "ds_cls =" <+> ppr c
- , text "ds_tys =" <+> ppr tys
- , text "ds_theta =" <+> ppr rhs
- , text "ds_mechanism =" <+> ppr mech ])
+ 2 (vcat [ text "ds_loc =" <+> ppr l
+ , text "ds_name =" <+> ppr n
+ , text "ds_tvs =" <+> ppr tvs
+ , text "ds_cls =" <+> ppr c
+ , text "ds_tys =" <+> ppr tys
+ , text "ds_theta =" <+> ppr rhs
+ , text "ds_standalone_wildcard =" <+> ppr wildcard
+ , text "ds_mechanism =" <+> ppr mech ])
instance Outputable theta => Outputable (DerivSpec theta) where
ppr = pprDerivSpec
@@ -129,7 +225,17 @@ data DerivSpecMechanism
| DerivSpecAnyClass -- -XDeriveAnyClass
-isDerivSpecStock, isDerivSpecNewtype, isDerivSpecAnyClass
+ | DerivSpecVia -- -XDerivingVia
+ Type -- The @via@ type
+
+-- | Convert a 'DerivSpecMechanism' to its corresponding 'DerivStrategy'.
+derivSpecMechanismToStrategy :: DerivSpecMechanism -> DerivStrategy GhcTc
+derivSpecMechanismToStrategy DerivSpecStock{} = StockStrategy
+derivSpecMechanismToStrategy DerivSpecNewtype{} = NewtypeStrategy
+derivSpecMechanismToStrategy DerivSpecAnyClass = AnyclassStrategy
+derivSpecMechanismToStrategy (DerivSpecVia t) = ViaStrategy t
+
+isDerivSpecStock, isDerivSpecNewtype, isDerivSpecAnyClass, isDerivSpecVia
:: DerivSpecMechanism -> Bool
isDerivSpecStock (DerivSpecStock{}) = True
isDerivSpecStock _ = False
@@ -137,96 +243,160 @@ isDerivSpecStock _ = False
isDerivSpecNewtype (DerivSpecNewtype{}) = True
isDerivSpecNewtype _ = False
-isDerivSpecAnyClass (DerivSpecAnyClass{}) = True
-isDerivSpecAnyClass _ = False
+isDerivSpecAnyClass DerivSpecAnyClass = True
+isDerivSpecAnyClass _ = False
--- A DerivSpecMechanism can be losslessly converted to a DerivStrategy.
-mechanismToStrategy :: DerivSpecMechanism -> DerivStrategy
-mechanismToStrategy (DerivSpecStock{}) = StockStrategy
-mechanismToStrategy (DerivSpecNewtype{}) = NewtypeStrategy
-mechanismToStrategy (DerivSpecAnyClass{}) = AnyclassStrategy
+isDerivSpecVia (DerivSpecVia{}) = True
+isDerivSpecVia _ = False
instance Outputable DerivSpecMechanism where
- ppr = ppr . mechanismToStrategy
-
-type DerivContext = Maybe ThetaType
- -- Nothing <=> Vanilla deriving; infer the context of the instance decl
- -- Just theta <=> Standalone deriving: context supplied by programmer
-
-data DerivStatus = CanDerive -- Stock class, can derive
- | DerivableClassError SDoc -- Stock class, but can't do it
- | DerivableViaInstance -- See Note [Deriving any class]
- | NonDerivableClass SDoc -- Non-stock class
+ ppr (DerivSpecStock{}) = text "DerivSpecStock"
+ ppr (DerivSpecNewtype t) = text "DerivSpecNewtype" <> colon <+> ppr t
+ ppr DerivSpecAnyClass = text "DerivSpecAnyClass"
+ ppr (DerivSpecVia t) = text "DerivSpecVia" <> colon <+> ppr t
+
+-- | Whether GHC is processing a @deriving@ clause or a standalone deriving
+-- declaration.
+data DerivContext
+ = InferContext (Maybe SrcSpan) -- ^ @'InferContext mb_wildcard@ is either:
+ --
+ -- * A @deriving@ clause (in which case
+ -- @mb_wildcard@ is 'Nothing').
+ --
+ -- * A standalone deriving declaration with
+ -- an extra-constraints wildcard as the
+ -- context (in which case @mb_wildcard@ is
+ -- @'Just' loc@, where @loc@ is the location
+ -- of the wildcard.
+ --
+ -- GHC should infer the context.
+
+ | SupplyContext ThetaType -- ^ @'SupplyContext' theta@ is a standalone
+ -- deriving declaration, where @theta@ is the
+ -- context supplied by the user.
+
+instance Outputable DerivContext where
+ ppr (InferContext standalone) = text "InferContext" <+> ppr standalone
+ ppr (SupplyContext theta) = text "SupplyContext" <+> ppr theta
+
+-- | Records whether a particular class can be derived by way of an
+-- /originative/ deriving strategy (i.e., @stock@ or @anyclass@).
+--
+-- See @Note [Deriving strategies]@ in "TcDeriv".
+data OriginativeDerivStatus
+ = CanDeriveStock -- Stock class, can derive
+ (SrcSpan -> TyCon -> [Type]
+ -> TcM (LHsBinds GhcPs, BagDerivStuff, [Name]))
+ | StockClassError SDoc -- Stock class, but can't do it
+ | CanDeriveAnyClass -- See Note [Deriving any class]
+ | NonDerivableClass SDoc -- Cannot derive with either stock or anyclass
-- A stock class is one either defined in the Haskell report or for which GHC
-- otherwise knows how to generate code for (possibly requiring the use of a
--- language extension), such as Eq, Ord, Ix, Data, Generic, etc.
+-- language extension), such as Eq, Ord, Ix, Data, Generic, etc.)
-- | A 'PredType' annotated with the origin of the constraint 'CtOrigin',
-- and whether or the constraint deals in types or kinds.
data PredOrigin = PredOrigin PredType CtOrigin TypeOrKind
--- | A list of wanted 'PredOrigin' constraints ('to_wanted_origins') alongside
--- any corresponding given constraints ('to_givens') and locally quantified
--- type variables ('to_tvs').
+-- | A list of wanted 'PredOrigin' constraints ('to_wanted_origins') to
+-- simplify when inferring a derived instance's context. These are used in all
+-- deriving strategies, but in the particular case of @DeriveAnyClass@, we
+-- need extra information. In particular, we need:
+--
+-- * 'to_anyclass_skols', the list of type variables bound by a class method's
+-- regular type signature, which should be rigid.
+--
+-- * 'to_anyclass_metas', the list of type variables bound by a class method's
+-- default type signature. These can be unified as necessary.
--
--- In most cases, 'to_givens' will be empty, as most deriving mechanisms (e.g.,
--- stock and newtype deriving) do not require given constraints. The exception
--- is @DeriveAnyClass@, which can involve given constraints. For example,
--- if you tried to derive an instance for the following class using
--- @DeriveAnyClass@:
+-- * 'to_anyclass_givens', the list of constraints from a class method's
+-- regular type signature, which can be used to help solve constraints
+-- in the 'to_wanted_origins'.
+--
+-- (Note that 'to_wanted_origins' will likely contain type variables from the
+-- derived type class or data type, neither of which will appear in
+-- 'to_anyclass_skols' or 'to_anyclass_metas'.)
+--
+-- For all other deriving strategies, it is always the case that
+-- 'to_anyclass_skols', 'to_anyclass_metas', and 'to_anyclass_givens' are
+-- empty.
+--
+-- Here is an example to illustrate this:
--
-- @
-- class Foo a where
--- bar :: a -> b -> String
--- default bar :: (Show a, Ix b) => a -> b -> String
--- bar = show
+-- bar :: forall b. Ix b => a -> b -> String
+-- default bar :: forall y. (Show a, Ix y) => a -> y -> String
+-- bar x y = show x ++ show (range (y, y))
--
-- baz :: Eq a => a -> a -> Bool
-- default baz :: Ord a => a -> a -> Bool
-- baz x y = compare x y == EQ
+--
+-- data Quux q = Quux deriving anyclass Foo
-- @
--
-- Then it would generate two 'ThetaOrigin's, one for each method:
--
-- @
--- [ ThetaOrigin { to_tvs = [b]
--- , to_givens = []
--- , to_wanted_origins = [Show a, Ix b] }
--- , ThetaOrigin { to_tvs = []
--- , to_givens = [Eq a]
--- , to_wanted_origins = [Ord a] }
+-- [ ThetaOrigin { to_anyclass_skols = [b]
+-- , to_anyclass_metas = [y]
+-- , to_anyclass_givens = [Ix b]
+-- , to_wanted_origins = [ Show (Quux q), Ix y
+-- , (Quux q -> b -> String) ~
+-- (Quux q -> y -> String)
+-- ] }
+-- , ThetaOrigin { to_anyclass_skols = []
+-- , to_anyclass_metas = []
+-- , to_anyclass_givens = [Eq (Quux q)]
+-- , to_wanted_origins = [ Ord (Quux q)
+-- , (Quux q -> Quux q -> Bool) ~
+-- (Quux q -> Quux q -> Bool)
+-- ] }
-- ]
-- @
+--
+-- (Note that the type variable @q@ is bound by the data type @Quux@, and thus
+-- it appears in neither 'to_anyclass_skols' nor 'to_anyclass_metas'.)
+--
+-- See @Note [Gathering and simplifying constraints for DeriveAnyClass]@
+-- in "TcDerivInfer" for an explanation of how 'to_wanted_origins' are
+-- determined in @DeriveAnyClass@, as well as how 'to_anyclass_skols',
+-- 'to_anyclass_metas', and 'to_anyclass_givens' are used.
data ThetaOrigin
- = ThetaOrigin { to_tvs :: [TyVar]
- , to_givens :: ThetaType
- , to_wanted_origins :: [PredOrigin] }
+ = ThetaOrigin { to_anyclass_skols :: [TyVar]
+ , to_anyclass_metas :: [TyVar]
+ , to_anyclass_givens :: ThetaType
+ , to_wanted_origins :: [PredOrigin] }
instance Outputable PredOrigin where
ppr (PredOrigin ty _ _) = ppr ty -- The origin is not so interesting when debugging
instance Outputable ThetaOrigin where
- ppr (ThetaOrigin { to_tvs = tvs
- , to_givens = givens
- , to_wanted_origins = wanted_origins })
+ ppr (ThetaOrigin { to_anyclass_skols = ac_skols
+ , to_anyclass_metas = ac_metas
+ , to_anyclass_givens = ac_givens
+ , to_wanted_origins = wanted_origins })
= hang (text "ThetaOrigin")
- 2 (vcat [ text "to_tvs =" <+> ppr tvs
- , text "to_givens =" <+> ppr givens
- , text "to_wanted_origins =" <+> ppr wanted_origins ])
+ 2 (vcat [ text "to_anyclass_skols =" <+> ppr ac_skols
+ , text "to_anyclass_metas =" <+> ppr ac_metas
+ , text "to_anyclass_givens =" <+> ppr ac_givens
+ , text "to_wanted_origins =" <+> ppr wanted_origins ])
mkPredOrigin :: CtOrigin -> TypeOrKind -> PredType -> PredOrigin
mkPredOrigin origin t_or_k pred = PredOrigin pred origin t_or_k
-mkThetaOrigin :: CtOrigin -> TypeOrKind -> [TyVar] -> ThetaType -> ThetaType
+mkThetaOrigin :: CtOrigin -> TypeOrKind
+ -> [TyVar] -> [TyVar] -> ThetaType -> ThetaType
-> ThetaOrigin
-mkThetaOrigin origin t_or_k tvs givens
- = ThetaOrigin tvs givens . map (mkPredOrigin origin t_or_k)
+mkThetaOrigin origin t_or_k skols metas givens
+ = ThetaOrigin skols metas givens . map (mkPredOrigin origin t_or_k)
-- A common case where the ThetaOrigin only contains wanted constraints, with
-- no givens or locally scoped type variables.
mkThetaOriginFromPreds :: [PredOrigin] -> ThetaOrigin
-mkThetaOriginFromPreds = ThetaOrigin [] []
+mkThetaOriginFromPreds = ThetaOrigin [] [] []
substPredOrigin :: HasCallStack => TCvSubst -> PredOrigin -> PredOrigin
substPredOrigin subst (PredOrigin pred origin t_or_k)
@@ -241,9 +411,9 @@ substPredOrigin subst (PredOrigin pred origin t_or_k)
Only certain blessed classes can be used in a deriving clause (without the
assistance of GeneralizedNewtypeDeriving or DeriveAnyClass). These classes
-are listed below in the definition of hasStockDeriving. The sideConditions
+are listed below in the definition of hasStockDeriving. The stockSideConditions
function determines the criteria that needs to be met in order for a particular
-class to be able to be derived successfully.
+stock class to be able to be derived successfully.
A class might be able to be used in a deriving clause if -XDeriveAnyClass
is willing to support it. The canDeriveAnyClass function checks if this is the
@@ -349,35 +519,43 @@ getDataConFixityFun tc
doc = text "Data con fixities for" <+> ppr name
------------------------------------------------------------------
--- Check side conditions that dis-allow derivability for particular classes
--- This is *apart* from the newtype-deriving mechanism
+-- Check side conditions that dis-allow derivability for the originative
+-- deriving strategies (stock and anyclass).
+-- See Note [Deriving strategies] in TcDeriv for an explanation of what
+-- "originative" means.
+--
+-- This is *apart* from the coerce-based strategies, newtype and via.
--
-- Here we get the representation tycon in case of family instances as it has
-- the data constructors - but we need to be careful to fall back to the
-- family tycon (with indexes) in error messages.
-checkSideConditions :: DynFlags -> DerivContext -> Class -> [TcType]
- -> TyCon -- tycon
- -> DerivStatus
-checkSideConditions dflags mtheta cls cls_tys rep_tc
- | Just cond <- sideConditions mtheta cls
- = case (cond dflags rep_tc) of
- NotValid err -> DerivableClassError err -- Class-specific error
+checkOriginativeSideConditions
+ :: DynFlags -> DerivContext -> Class -> [TcType]
+ -> TyCon -> TyCon
+ -> OriginativeDerivStatus
+checkOriginativeSideConditions dflags deriv_ctxt cls cls_tys tc rep_tc
+ -- First, check if stock deriving is possible...
+ | Just cond <- stockSideConditions deriv_ctxt cls
+ = case (cond dflags tc rep_tc) of
+ NotValid err -> StockClassError err -- Class-specific error
IsValid | null (filterOutInvisibleTypes (classTyCon cls) cls_tys)
- -> CanDerive
-- All stock derivable classes are unary in the sense that
-- there should be not types in cls_tys (i.e., no type args
-- other than last). Note that cls_types can contain
-- invisible types as well (e.g., for Generic1, which is
-- poly-kinded), so make sure those are not counted.
- | otherwise -> DerivableClassError (classArgsErr cls cls_tys)
+ , Just gen_fn <- hasStockDeriving cls
+ -> CanDeriveStock gen_fn
+ | otherwise -> StockClassError (classArgsErr cls cls_tys)
-- e.g. deriving( Eq s )
+ -- ...if not, try falling back on DeriveAnyClass.
| NotValid err <- canDeriveAnyClass dflags
- = NonDerivableClass err -- DeriveAnyClass does not work
+ = NonDerivableClass err -- Neither anyclass nor stock work
| otherwise
- = DerivableViaInstance -- DeriveAnyClass should work
+ = CanDeriveAnyClass -- DeriveAnyClass should work
classArgsErr :: Class -> [Type] -> SDoc
classArgsErr cls cls_tys = quotes (ppr (mkClassPred cls cls_tys)) <+> text "is not a class"
@@ -387,8 +565,8 @@ classArgsErr cls cls_tys = quotes (ppr (mkClassPred cls cls_tys)) <+> text "is n
-- mechanism on certain classes (as opposed to classes that require
-- GeneralizedNewtypeDeriving or DeriveAnyClass). Returns Nothing for a
-- class for which stock deriving isn't possible.
-sideConditions :: DerivContext -> Class -> Maybe Condition
-sideConditions mtheta cls
+stockSideConditions :: DerivContext -> Class -> Maybe Condition
+stockSideConditions deriv_ctxt cls
| cls_key == eqClassKey = Just (cond_std `andCond` cond_args cls)
| cls_key == ordClassKey = Just (cond_std `andCond` cond_args cls)
| cls_key == showClassKey = Just (cond_std `andCond` cond_args cls)
@@ -397,7 +575,7 @@ sideConditions mtheta cls
| cls_key == ixClassKey = Just (cond_std `andCond` cond_enumOrProduct cls)
| cls_key == boundedClassKey = Just (cond_std `andCond` cond_enumOrProduct cls)
| cls_key == dataClassKey = Just (checkFlag LangExt.DeriveDataTypeable `andCond`
- cond_std `andCond`
+ cond_vanilla `andCond`
cond_args cls)
| cls_key == functorClassKey = Just (checkFlag LangExt.DeriveFunctor `andCond`
cond_vanilla `andCond`
@@ -422,10 +600,10 @@ sideConditions mtheta cls
| otherwise = Nothing
where
cls_key = getUnique cls
- cond_std = cond_stdOK mtheta False -- Vanilla data constructors, at least one,
- -- and monotype arguments
- cond_vanilla = cond_stdOK mtheta True -- Vanilla data constructors but
- -- allow no data cons or polytype arguments
+ cond_std = cond_stdOK deriv_ctxt False
+ -- Vanilla data constructors, at least one, and monotype arguments
+ cond_vanilla = cond_stdOK deriv_ctxt True
+ -- Vanilla data constructors but allow no data cons or polytype arguments
canDeriveAnyClass :: DynFlags -> Validity
-- IsValid: we can (try to) derive it via an empty instance declaration
@@ -436,49 +614,108 @@ canDeriveAnyClass dflags
| otherwise
= IsValid -- OK!
-type Condition = DynFlags -> TyCon -> Validity
- -- TyCon is the *representation* tycon if the data type is an indexed one
- -- Nothing => OK
+type Condition
+ = DynFlags
+
+ -> TyCon -- ^ The data type's 'TyCon'. For data families, this is the
+ -- family 'TyCon'.
+
+ -> TyCon -- ^ For data families, this is the representation 'TyCon'.
+ -- Otherwise, this is the same as the other 'TyCon' argument.
+
+ -> Validity -- ^ 'IsValid' if deriving an instance for this 'TyCon' is
+ -- possible. Otherwise, it's @'NotValid' err@, where @err@
+ -- explains what went wrong.
orCond :: Condition -> Condition -> Condition
-orCond c1 c2 dflags tc
- = case (c1 dflags tc, c2 dflags tc) of
+orCond c1 c2 dflags tc rep_tc
+ = case (c1 dflags tc rep_tc, c2 dflags tc rep_tc) of
(IsValid, _) -> IsValid -- c1 succeeds
(_, IsValid) -> IsValid -- c21 succeeds
(NotValid x, NotValid y) -> NotValid (x $$ text " or" $$ y)
-- Both fail
andCond :: Condition -> Condition -> Condition
-andCond c1 c2 dflags tc = c1 dflags tc `andValid` c2 dflags tc
-
-cond_stdOK :: DerivContext -- Says whether this is standalone deriving or not;
- -- if standalone, we just say "yes, go for it"
- -> Bool -- True <=> permissive: allow higher rank
- -- args and no data constructors
- -> Condition
-cond_stdOK (Just _) _ _ _
- = IsValid -- Don't check these conservative conditions for
+andCond c1 c2 dflags tc rep_tc
+ = c1 dflags tc rep_tc `andValid` c2 dflags tc rep_tc
+
+-- | Some common validity checks shared among stock derivable classes. One
+-- check that absolutely must hold is that if an instance @C (T a)@ is being
+-- derived, then @T@ must be a tycon for a data type or a newtype. The
+-- remaining checks are only performed if using a @deriving@ clause (i.e.,
+-- they're ignored if using @StandaloneDeriving@):
+--
+-- 1. The data type must have at least one constructor (this check is ignored
+-- if using @EmptyDataDeriving@).
+--
+-- 2. The data type cannot have any GADT constructors.
+--
+-- 3. The data type cannot have any constructors with existentially quantified
+-- type variables.
+--
+-- 4. The data type cannot have a context (e.g., @data Foo a = Eq a => MkFoo@).
+--
+-- 5. The data type cannot have fields with higher-rank types.
+cond_stdOK
+ :: DerivContext -- ^ 'SupplyContext' if this is standalone deriving with a
+ -- user-supplied context, 'InferContext' if not.
+ -- If it is the former, we relax some of the validity checks
+ -- we would otherwise perform (i.e., "just go for it").
+
+ -> Bool -- ^ 'True' <=> allow higher rank arguments and empty data
+ -- types (with no data constructors) even in the absence of
+ -- the -XEmptyDataDeriving extension.
+
+ -> Condition
+cond_stdOK deriv_ctxt permissive dflags tc rep_tc
+ = valid_ADT `andValid` valid_misc
+ where
+ valid_ADT, valid_misc :: Validity
+ valid_ADT
+ | isAlgTyCon tc || isDataFamilyTyCon tc
+ = IsValid
+ | otherwise
+ -- Complain about functions, primitive types, and other tycons that
+ -- stock deriving can't handle.
+ = NotValid $ text "The last argument of the instance must be a"
+ <+> text "data or newtype application"
+
+ valid_misc
+ = case deriv_ctxt of
+ SupplyContext _ -> IsValid
+ -- Don't check these conservative conditions for
-- standalone deriving; just generate the code
-- and let the typechecker handle the result
-cond_stdOK Nothing permissive _ rep_tc
- | null data_cons
- , not permissive = NotValid (no_cons_why rep_tc $$ suggestion)
- | not (null con_whys) = NotValid (vcat con_whys $$ suggestion)
- | otherwise = IsValid
- where
- suggestion = text "Possible fix: use a standalone deriving declaration instead"
+ InferContext wildcard
+ | null data_cons -- 1.
+ , not permissive
+ -> checkFlag LangExt.EmptyDataDeriving dflags tc rep_tc `orValid`
+ NotValid (no_cons_why rep_tc $$ empty_data_suggestion)
+ | not (null con_whys)
+ -> NotValid (vcat con_whys $$ possible_fix_suggestion wildcard)
+ | otherwise
+ -> IsValid
+
+ empty_data_suggestion =
+ text "Use EmptyDataDeriving to enable deriving for empty data types"
+ possible_fix_suggestion wildcard
+ = case wildcard of
+ Just _ ->
+ text "Possible fix: fill in the wildcard constraint yourself"
+ Nothing ->
+ text "Possible fix: use a standalone deriving declaration instead"
data_cons = tyConDataCons rep_tc
con_whys = getInvalids (map check_con data_cons)
check_con :: DataCon -> Validity
check_con con
- | not (null eq_spec)
+ | not (null eq_spec) -- 2.
= bad "is a GADT"
- | not (null ex_tvs)
+ | not (null ex_tvs) -- 3.
= bad "has existential type variables in its type"
- | not (null theta)
+ | not (null theta) -- 4.
= bad "has constraints in its type"
- | not (permissive || all isTauTy (dataConOrigArgTys con))
+ | not (permissive || all isTauTy (dataConOrigArgTys con)) -- 5.
= bad "has a higher-rank type"
| otherwise
= IsValid
@@ -491,10 +728,10 @@ no_cons_why rep_tc = quotes (pprSourceTyCon rep_tc) <+>
text "must have at least one data constructor"
cond_RepresentableOk :: Condition
-cond_RepresentableOk _ tc = canDoGenerics tc
+cond_RepresentableOk _ _ rep_tc = canDoGenerics rep_tc
cond_Representable1Ok :: Condition
-cond_Representable1Ok _ tc = canDoGenerics1 tc
+cond_Representable1Ok _ _ rep_tc = canDoGenerics1 rep_tc
cond_enumOrProduct :: Class -> Condition
cond_enumOrProduct cls = cond_isEnumeration `orCond`
@@ -503,13 +740,13 @@ cond_enumOrProduct cls = cond_isEnumeration `orCond`
cond_args :: Class -> Condition
-- For some classes (eg Eq, Ord) we allow unlifted arg types
-- by generating specialised code. For others (eg Data) we don't.
-cond_args cls _ tc
+cond_args cls _ _ rep_tc
= case bad_args of
[] -> IsValid
(ty:_) -> NotValid (hang (text "Don't know how to derive" <+> quotes (ppr cls))
2 (text "for type" <+> quotes (ppr ty)))
where
- bad_args = [ arg_ty | con <- tyConDataCons tc
+ bad_args = [ arg_ty | con <- tyConDataCons rep_tc
, arg_ty <- dataConOrigArgTys con
, isUnliftedType arg_ty
, not (ok_ty arg_ty) ]
@@ -527,7 +764,7 @@ cond_args cls _ tc
cond_isEnumeration :: Condition
-cond_isEnumeration _ rep_tc
+cond_isEnumeration _ _ rep_tc
| isEnumerationTyCon rep_tc = IsValid
| otherwise = NotValid why
where
@@ -537,7 +774,7 @@ cond_isEnumeration _ rep_tc
-- See Note [Enumeration types] in TyCon
cond_isProduct :: Condition
-cond_isProduct _ rep_tc
+cond_isProduct _ _ rep_tc
| isProductTyCon rep_tc = IsValid
| otherwise = NotValid why
where
@@ -551,7 +788,7 @@ cond_functorOK :: Bool -> Bool -> Condition
-- (c) don't use argument in the wrong place, e.g. data T a = T (X a a)
-- (d) optionally: don't use function types
-- (e) no "stupid context" on data type
-cond_functorOK allowFunctions allowExQuantifiedLastTyVar _ rep_tc
+cond_functorOK allowFunctions allowExQuantifiedLastTyVar _ _ rep_tc
| null tc_tvs
= NotValid (text "Data type" <+> quotes (ppr rep_tc)
<+> text "must have some type parameters")
@@ -600,7 +837,7 @@ cond_functorOK allowFunctions allowExQuantifiedLastTyVar _ rep_tc
wrong_arg = text "must use the type variable only as the last argument of a data type"
checkFlag :: LangExt.Extension -> Condition
-checkFlag flag dflags _
+checkFlag flag dflags _ _
| xopt flag dflags = IsValid
| otherwise = NotValid why
where