summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/GHC/Hs/Expr.hs2
-rw-r--r--compiler/GHC/HsToCore/PmCheck.hs86
-rw-r--r--compiler/GHC/HsToCore/PmCheck/Oracle.hs2174
-rw-r--r--compiler/GHC/HsToCore/PmCheck/Ppr.hs6
-rw-r--r--compiler/GHC/HsToCore/PmCheck/Types.hs96
-rw-r--r--compiler/GHC/HsToCore/PmCheck/Types.hs-boot9
-rw-r--r--compiler/GHC/HsToCore/Types.hs2
-rw-r--r--compiler/GHC/Tc/Gen/Expr.hs2
8 files changed, 1152 insertions, 1225 deletions
diff --git a/compiler/GHC/Hs/Expr.hs b/compiler/GHC/Hs/Expr.hs
index 466260c782..251fae2c48 100644
--- a/compiler/GHC/Hs/Expr.hs
+++ b/compiler/GHC/Hs/Expr.hs
@@ -1347,8 +1347,10 @@ hsExprNeedsParens p = go
ExpansionExpr (HsExpanded a _) -> hsExprNeedsParens p a
| GhcRn <- ghcPass @p
= case x of HsExpanded a _ -> hsExprNeedsParens p a
+#if __GLASGOW_HASKELL__ <= 900
| otherwise
= True
+#endif
-- | @'parenthesizeHsExpr' p e@ checks if @'hsExprNeedsParens' p e@ is true,
diff --git a/compiler/GHC/HsToCore/PmCheck.hs b/compiler/GHC/HsToCore/PmCheck.hs
index 831509d351..bad245cc6e 100644
--- a/compiler/GHC/HsToCore/PmCheck.hs
+++ b/compiler/GHC/HsToCore/PmCheck.hs
@@ -781,28 +781,6 @@ This means we can't just desugar the pattern match to
@[T a b <- x, 'a' <- a, 42 <- b]@. Instead we have to force them in the
right order: @[T a b <- x, 42 <- b, 'a' <- a]@.
-Note [Strict fields and fields of unlifted type]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-How do strict fields play into Note [Field match order for RecCon]? Answer:
-They don't. Desugaring is entirely unconcerned by strict fields; the forcing
-happens *before* pattern matching. But for each strict (or more generally,
-unlifted) field @s@ we have to add @s ≁ ⊥@ constraints when we check the PmCon
-guard in 'checkGrd'. Strict fields are devoid of ⊥ by construction, there's
-nothing that a bang pattern would act on. Example from #18341:
-
- data T = MkT !Int
- f :: T -> ()
- f (MkT _) | False = () -- inaccessible
- f (MkT !_) | False = () -- redundant, not only inaccessible!
- f _ = ()
-
-The second clause desugars to @MkT n <- x, !n@. When coverage checked, the
-'PmCon' @MkT n <- x@ refines the set of values that reach the bang pattern with
-the constraints @x ~ MkT n, n ≁ ⊥@ (this list is computed by 'pmConCts').
-Checking the 'PmBang' @!n@ will then try to add the constraint @n ~ ⊥@ to this
-set to get the diverging set, which is found to be empty. Hence the whole
-clause is detected as redundant, as expected.
-
Note [Order of guards matters]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Similar to Note [Field match order for RecCon], the order in which the guards
@@ -872,17 +850,17 @@ instance Outputable a => Outputable (CheckResult a) where
field name value = text name <+> equals <+> ppr value
-- | Lift 'addPmCts' over 'Nablas'.
-addPmCtsNablas :: Nablas -> PmCts -> DsM Nablas
-addPmCtsNablas nablas cts = liftNablasM (\d -> addPmCts d cts) nablas
+addPhiCtsNablas :: Nablas -> PhiCts -> DsM Nablas
+addPhiCtsNablas nablas cts = liftNablasM (\d -> addPhiCts d cts) nablas
-- | 'addPmCtsNablas' for a single 'PmCt'.
-addPmCtNablas :: Nablas -> PmCt -> DsM Nablas
-addPmCtNablas nablas ct = addPmCtsNablas nablas (unitBag ct)
+addPhiCtNablas :: Nablas -> PhiCt -> DsM Nablas
+addPhiCtNablas nablas ct = addPhiCtsNablas nablas (unitBag ct)
-- | Test if any of the 'Nabla's is inhabited. Currently this is pure, because
-- we preserve the invariant that there are no uninhabited 'Nabla's. But that
-- could change in the future, for example by implementing this function in
--- terms of @notNull <$> provideEvidence 1 ds@.
+-- terms of @notNull <$> generateInhabitingPatterns 1 ds@.
isInhabited :: Nablas -> DsM Bool
isInhabited (MkNablas ds) = pure (not (null ds))
@@ -938,26 +916,6 @@ throttle limit old@(MkNablas old_ds) new@(MkNablas new_ds)
| length new_ds > max limit (length old_ds) = (Approximate, old)
| otherwise = (Precise, new)
--- | The 'PmCts' arising from a successful 'PmCon' match @T gammas as ys <- x@.
--- These include
---
--- * @gammas@: Constraints arising from the bound evidence vars
--- * @y ≁ ⊥@ constraints for each unlifted field (including strict fields)
--- @y@ in @ys@
--- * The constructor constraint itself: @x ~ T as ys@.
---
--- See Note [Strict fields and fields of unlifted type].
-pmConCts :: Id -> PmAltCon -> [TyVar] -> [EvVar] -> [Id] -> PmCts
-pmConCts x con tvs dicts args = gammas `unionBags` unlifted `snocBag` con_ct
- where
- gammas = listToBag $ map (PmTyCt . evVarPred) dicts
- con_ct = PmConCt x con tvs args
- unlifted = listToBag [ PmNotBotCt arg
- | (arg, bang) <-
- zipEqual "pmConCts" args (pmAltConImplBangs con)
- , isBanged bang || isUnliftedType (idType arg)
- ]
-
checkSequence :: (grdtree -> CheckAction anntree) -> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
-- The implementation is pretty similar to
-- @traverse1 :: Apply f => (a -> f b) -> NonEmpty a -> f (NonEmpty b)@
@@ -969,32 +927,37 @@ checkGrd :: PmGrd -> CheckAction RedSets
checkGrd grd = CA $ \inc -> case grd of
-- let x = e: Refine with x ~ e
PmLet x e -> do
- matched <- addPmCtNablas inc (PmCoreCt x e)
- -- tracePm "check:Let" (ppr x <+> char '=' <+> ppr e)
+ matched <- addPhiCtNablas inc (PhiCoreCt x e)
+ tracePm "check:Let" (ppr x <+> char '=' <+> ppr e)
pure CheckResult { cr_ret = emptyRedSets { rs_cov = matched }
, cr_uncov = mempty
, cr_approx = Precise }
-- Bang x _: Diverge on x ~ ⊥, refine with x ≁ ⊥
PmBang x mb_info -> do
- div <- addPmCtNablas inc (PmBotCt x)
- matched <- addPmCtNablas inc (PmNotBotCt x)
+ div <- addPhiCtNablas inc (PhiBotCt x)
+ matched <- addPhiCtNablas inc (PhiNotBotCt x)
-- See Note [Dead bang patterns]
-- mb_info = Just info <==> PmBang originates from bang pattern in source
let bangs | Just info <- mb_info = unitOL (div, info)
| otherwise = NilOL
- -- tracePm "check:Bang" (ppr x <+> ppr div)
+ tracePm "check:Bang" (ppr x <+> ppr div)
pure CheckResult { cr_ret = RedSets { rs_cov = matched, rs_div = div, rs_bangs = bangs }
, cr_uncov = mempty
, cr_approx = Precise }
-- Con: Fall through on x ≁ K and refine with x ~ K ys and type info
PmCon x con tvs dicts args -> do
!div <- if isPmAltConMatchStrict con
- then addPmCtNablas inc (PmBotCt x)
+ then addPhiCtNablas inc (PhiBotCt x)
else pure mempty
- let con_cts = pmConCts x con tvs dicts args
- !matched <- addPmCtsNablas inc con_cts
- !uncov <- addPmCtNablas inc (PmNotConCt x con)
- -- tracePm "checkGrd:Con" (ppr inc $$ ppr grd $$ ppr con_cts $$ ppr matched)
+ !matched <- addPhiCtNablas inc (PhiConCt x con tvs (map evVarPred dicts) args)
+ !uncov <- addPhiCtNablas inc (PhiNotConCt x con)
+ tracePm "check:Con" $ vcat
+ [ ppr grd
+ , ppr inc
+ , hang (text "div") 2 (ppr div)
+ , hang (text "matched") 2 (ppr matched)
+ , hang (text "uncov") 2 (ppr uncov)
+ ]
pure CheckResult { cr_ret = emptyRedSets { rs_cov = matched, rs_div = div }
, cr_uncov = uncov
, cr_approx = Precise }
@@ -1028,7 +991,7 @@ checkGRHS (PmGRHS { pg_grds = grds, pg_rhs = rhs_info }) =
checkEmptyCase :: PmEmptyCase -> CheckAction PmEmptyCase
checkEmptyCase pe@(PmEmptyCase { pe_var = var }) = CA $ \inc -> do
- unc <- addPmCtNablas inc (PmNotBotCt var)
+ unc <- addPhiCtNablas inc (PhiNotBotCt var)
pure CheckResult { cr_ret = pe, cr_uncov = unc, cr_approx = mempty }
checkPatBind :: (PmPatBind Pre) -> CheckAction (PmPatBind Post)
@@ -1275,7 +1238,7 @@ getNFirstUncovered vars n (MkNablas nablas) = go n (bagToList nablas)
go 0 _ = pure []
go _ [] = pure []
go n (nabla:nablas) = do
- front <- provideEvidence vars n nabla
+ front <- generateInhabitingPatterns vars n nabla
back <- go (n - length front) nablas
pure (front ++ back)
@@ -1415,7 +1378,8 @@ addTyCs :: Origin -> Bag EvVar -> DsM a -> DsM a
addTyCs origin ev_vars m = do
dflags <- getDynFlags
applyWhen (needToRunPmCheck dflags origin)
- (locallyExtendPmNablas (\nablas -> addPmCtsNablas nablas (PmTyCt . evVarPred <$> ev_vars)))
+ (locallyExtendPmNablas $ \nablas ->
+ addPhiCtsNablas nablas (PhiTyCt . evVarPred <$> ev_vars))
m
-- | Add equalities for the 'CoreExpr' scrutinee to the local 'DsM' environment
@@ -1427,7 +1391,7 @@ addCoreScrutTmCs :: Maybe CoreExpr -> [Id] -> DsM a -> DsM a
addCoreScrutTmCs Nothing _ k = k
addCoreScrutTmCs (Just scr) [x] k =
flip locallyExtendPmNablas k $ \nablas ->
- addPmCtsNablas nablas (unitBag (PmCoreCt x scr))
+ addPhiCtsNablas nablas (unitBag (PhiCoreCt x scr))
addCoreScrutTmCs _ _ _ = panic "addCoreScrutTmCs: scrutinee, but more than one match id"
-- | 'addCoreScrutTmCs', but desugars the 'LHsExpr' first.
diff --git a/compiler/GHC/HsToCore/PmCheck/Oracle.hs b/compiler/GHC/HsToCore/PmCheck/Oracle.hs
index 492e993f19..fd76bcf70d 100644
--- a/compiler/GHC/HsToCore/PmCheck/Oracle.hs
+++ b/compiler/GHC/HsToCore/PmCheck/Oracle.hs
@@ -4,26 +4,25 @@ Authors: George Karachalias <george.karachalias@cs.kuleuven.be>
Ryan Scott <ryan.gl.scott@gmail.com>
-}
-{-# LANGUAGE CPP, LambdaCase, TupleSections, PatternSynonyms, ViewPatterns, MultiWayIf, ScopedTypeVariables #-}
+{-# LANGUAGE CPP, LambdaCase, TupleSections, PatternSynonyms, ViewPatterns,
+ MultiWayIf, ScopedTypeVariables, MagicHash #-}
-- | The pattern match oracle. The main export of the module are the functions
--- 'addPmCts' for adding facts to the oracle, and 'provideEvidence' to turn a
+-- 'addPhiCts' for adding facts to the oracle, and 'generateInhabitingPatterns' to turn a
-- 'Nabla' into a concrete evidence for an equation.
--
-- In terms of the [Lower Your Guards paper](https://dl.acm.org/doi/abs/10.1145/3408989)
-- describing the implementation, this module is concerned with Sections 3.4, 3.6 and 3.7.
--- E.g., it represents refinement types diretly as a normalised refinement type 'Nabla'.
+-- E.g., it represents refinement types directly as a normalised refinement type 'Nabla'.
module GHC.HsToCore.PmCheck.Oracle (
DsM, tracePm, mkPmId,
Nabla, initNablas, lookupRefuts, lookupSolution,
- PmCt(PmTyCt), PmCts, pattern PmVarCt, pattern PmCoreCt,
- pattern PmConCt, pattern PmNotConCt, pattern PmBotCt,
- pattern PmNotBotCt,
+ PhiCt(..), PhiCts,
- addPmCts, -- Add a constraint to the oracle.
- provideEvidence
+ addPhiCts, -- Add a constraint to the oracle.
+ generateInhabitingPatterns
) where
#include "HsVersions.h"
@@ -39,14 +38,15 @@ import GHC.Utils.Error
import GHC.Utils.Misc
import GHC.Utils.Panic
import GHC.Data.Bag
+import GHC.Types.Unique
import GHC.Types.Unique.Set
import GHC.Types.Unique.DSet
-import GHC.Types.Unique
-import GHC.Types.Id
-import GHC.Types.Var.Env
import GHC.Types.Unique.DFM
-import GHC.Types.Var (EvVar)
+import GHC.Types.Id
import GHC.Types.Name
+import GHC.Types.Var (EvVar)
+import GHC.Types.Var.Env
+import GHC.Types.Var.Set
import GHC.Core
import GHC.Core.FVs (exprFreeVars)
import GHC.Core.Map
@@ -74,17 +74,18 @@ import GHC.Tc.Instance.Family
import GHC.Core.FamInstEnv
import Control.Applicative ((<|>))
-import Control.Monad (guard, mzero, when)
+import Control.Monad (foldM, forM, guard, mzero, when)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Strict
-import Data.Bifunctor (second)
import Data.Either (partitionEithers)
import Data.Foldable (foldlM, minimumBy, toList)
-import Data.List (find)
+import Data.List (sortBy, find)
+import qualified Data.List.NonEmpty as NE
import Data.Ord (comparing)
-import qualified Data.Semigroup as Semigroup
import Data.Tuple (swap)
+import GHC.Driver.Ppr (pprTrace)
+
-- Debugging Infrastructure
tracePm :: String -> SDoc -> DsM ()
@@ -95,6 +96,17 @@ tracePm herald doc = do
Opt_D_dump_ec_trace "" FormatText (text herald $$ (nest 2 doc))
{-# INLINE tracePm #-} -- see Note [INLINE conditional tracing utilities]
+debugOn :: () -> Bool
+debugOn _ = False
+-- debugOn _ = True
+
+trc :: String -> SDoc -> a -> a
+trc | debugOn () = pprTrace
+ | otherwise = \_ _ a -> a
+
+_trcM :: Monad m => String -> SDoc -> m ()
+_trcM header doc = trc header doc (return ())
+
-- | Generate a fresh `Id` of a given type
mkPmId :: Type -> DsM Id
mkPmId ty = getUniqueM >>= \unique ->
@@ -103,14 +115,10 @@ mkPmId ty = getUniqueM >>= \unique ->
in return (mkLocalIdOrCoVar name Many ty)
-----------------------------------------------
--- * Caching possible matches of a COMPLETE set
+-- * Caching residual COMPLETE set
-- See Note [Implementation of COMPLETE pragmas]
--- | Traverse the COMPLETE sets of 'ResidualCompleteMatches'.
-trvRcm :: Applicative f => (ConLikeSet -> f ConLikeSet) -> ResidualCompleteMatches -> f ResidualCompleteMatches
-trvRcm f (RCM vanilla pragmas) = RCM <$> traverse f vanilla
- <*> traverse (traverse f) pragmas
-- | Update the COMPLETE sets of 'ResidualCompleteMatches'.
updRcm :: (ConLikeSet -> ConLikeSet) -> ResidualCompleteMatches -> ResidualCompleteMatches
updRcm f (RCM vanilla pragmas) = RCM (f <$> vanilla) (fmap f <$> pragmas)
@@ -191,7 +199,7 @@ The pattern-match checker will then initialise each variable's 'VarInfo' with
well-typed or not, into a 'ResidualCompleteMatches'. The trick is that a
COMPLETE set that is ill-typed for that match variable could never be written by
the user! And we make sure not to report any ill-typed COMPLETE sets when
-formatting 'Nabla's for warnings in 'provideEvidence'.
+formatting 'Nabla's for warnings in 'generateInhabitingPatterns'.
A 'ResidualCompleteMatches' is a list of all COMPLETE sets, minus the ConLikes
we know a particular variable can't be (through negative constructor constraints
@@ -214,124 +222,6 @@ that the match in the guards of @f@ is exhaustive, where the COMPLETE set
applies due to refined type information.
-}
----------------------------------------------------
--- * Instantiating constructors, types and evidence
-
--- | Instantiate a 'ConLike' given its universal type arguments. Instantiates
--- existential and term binders with fresh variables of appropriate type.
--- Returns instantiated type and term variables from the match, type evidence
--- and the types of strict constructor fields.
-mkOneConFull :: [Type] -> ConLike -> DsM ([TyVar], [Id], Bag TyCt, [Type])
--- * 'con' K is a ConLike
--- - In the case of DataCons and most PatSynCons, these
--- are associated with a particular TyCon T
--- - But there are PatSynCons for this is not the case! See #11336, #17112
---
--- * 'arg_tys' tys are the types K's universally quantified type
--- variables should be instantiated to.
--- - For DataCons and most PatSyns these are the arguments of their TyCon
--- - For cases like the PatSyns in #11336, #17112, we can't easily guess
--- these, so don't call this function.
---
--- After instantiating the universal tyvars of K to tys we get
--- K @tys :: forall bs. Q => s1 .. sn -> T tys
--- Note that if K is a PatSynCon, depending on arg_tys, T might not necessarily
--- be a concrete TyCon.
---
--- Suppose y1 is a strict field. Then we get
--- Results: bs
--- [y1,..,yn]
--- Q
--- [s1]
-mkOneConFull arg_tys con = do
- let (univ_tvs, ex_tvs, eq_spec, thetas, _req_theta, field_tys, _con_res_ty)
- = conLikeFullSig con
- -- pprTrace "mkOneConFull" (ppr con $$ ppr arg_tys $$ ppr univ_tvs $$ ppr _con_res_ty) (return ())
- -- Substitute universals for type arguments
- let subst_univ = zipTvSubst univ_tvs arg_tys
- -- Instantiate fresh existentials as arguments to the constructor. This is
- -- important for instantiating the Thetas and field types.
- (subst, _) <- cloneTyVarBndrs subst_univ ex_tvs <$> getUniqueSupplyM
- let field_tys' = substTys subst $ map scaledThing field_tys
- -- Instantiate fresh term variables (VAs) as arguments to the constructor
- vars <- mapM mkPmId field_tys'
- -- All constraints bound by the constructor (alpha-renamed), these are added
- -- to the type oracle
- let ty_cs = substTheta subst (eqSpecPreds eq_spec ++ thetas)
- -- Figure out the types of strict constructor fields
- let arg_is_strict = map isBanged $ conLikeImplBangs con
- strict_arg_tys = filterByList arg_is_strict field_tys'
- return (ex_tvs, vars, listToBag ty_cs, strict_arg_tys)
-
--------------------------
--- * Pattern match oracle
-
-
--------------------------------------
--- * Composable satisfiability checks
-
--- | Given a 'Nabla', check if it is compatible with new facts encoded in this
--- this check. If so, return 'Just' a potentially extended 'Nabla'. Return
--- 'Nothing' if unsatisfiable.
---
--- There are three essential SatisfiabilityChecks:
--- 1. 'tmIsSatisfiable', adding term oracle facts
--- 2. 'tyIsSatisfiable', adding type oracle facts
--- 3. 'tysAreNonVoid', checks if the given types have an inhabitant
--- Functions like 'pmIsSatisfiable', 'nonVoid' and 'testInhabited' plug these
--- together as they see fit.
-newtype SatisfiabilityCheck = SC (Nabla -> DsM (Maybe Nabla))
-
--- | Check the given 'Nabla' for satisfiability by the given
--- 'SatisfiabilityCheck'. Return 'Just' a new, potentially extended, 'Nabla' if
--- successful, and 'Nothing' otherwise.
-runSatisfiabilityCheck :: Nabla -> SatisfiabilityCheck -> DsM (Maybe Nabla)
-runSatisfiabilityCheck nabla (SC chk) = chk nabla
-
--- | Allowing easy composition of 'SatisfiabilityCheck's.
-instance Semigroup SatisfiabilityCheck where
- -- This is @a >=> b@ from MaybeT DsM
- SC a <> SC b = SC c
- where
- c nabla = a nabla >>= \case
- Nothing -> pure Nothing
- Just nabla' -> b nabla'
-
-instance Monoid SatisfiabilityCheck where
- -- We only need this because of mconcat (which we use in place of sconcat,
- -- which requires NonEmpty lists as argument, making all call sites ugly)
- mempty = SC (pure . Just)
-
--------------------------------
--- * Oracle transition function
-
--- | Given a conlike's term constraints, type constraints, and strict argument
--- types, check if they are satisfiable.
--- (In other words, this is the ⊢_Sat oracle judgment from the GADTs Meet
--- Their Match paper.)
---
--- Taking strict argument types into account is something which was not
--- discussed in GADTs Meet Their Match. For an explanation of what role they
--- serve, see @Note [Strict argument type constraints]@.
-pmIsSatisfiable
- :: Nabla -- ^ The ambient term and type constraints
- -- (known to be satisfiable).
- -> Bag TyCt -- ^ The new type constraints.
- -> Bag TmCt -- ^ The new term constraints.
- -> [Type] -- ^ The strict argument types.
- -> DsM (Maybe Nabla)
- -- ^ @'Just' nabla@ if the constraints (@nabla@) are
- -- satisfiable, and each strict argument type is inhabitable.
- -- 'Nothing' otherwise.
-pmIsSatisfiable amb_cs new_ty_cs new_tm_cs strict_arg_tys =
- -- The order is important here! Check the new type constraints before we check
- -- whether strict argument types are inhabited given those constraints.
- runSatisfiabilityCheck amb_cs $ mconcat
- [ tyIsSatisfiable True new_ty_cs
- , tmIsSatisfiable new_tm_cs
- , tysAreNonVoid initRecTc strict_arg_tys
- ]
-
-----------------------
-- * Type normalisation
@@ -359,15 +249,9 @@ data TopNormaliseTypeResult
-- in the chain of reduction steps between the Source type and the Core type.
-- We also keep the type of the DataCon application and its field, so that we
-- don't have to reconstruct it in 'inhabitationCandidates' and
- -- 'provideEvidence'.
+ -- 'generateInhabitingPatterns'.
-- For an example, see Note [Type normalisation].
--- | Just give me the potentially normalised source type, unchanged or not!
-normalisedSourceType :: TopNormaliseTypeResult -> Type
-normalisedSourceType (NoChange ty) = ty
-normalisedSourceType (NormalisedByConstraints ty) = ty
-normalisedSourceType (HadRedexes ty _ _) = ty
-
-- | Return the fields of 'HadRedexes'. Returns appropriate defaults in the
-- other cases.
tntrGuts :: TopNormaliseTypeResult -> (Type, [(Type, DataCon, Type)], Type)
@@ -400,8 +284,9 @@ pmTopNormaliseType :: TyState -> Type -> DsM TopNormaliseTypeResult
-- NB: Normalisation can potentially change kinds, if the head of the type
-- is a type family with a variable result kind. I (Richard E) can't think
-- of a way to cause trouble here, though.
-pmTopNormaliseType (TySt inert) typ
+pmTopNormaliseType (TySt _ inert) typ
= do env <- dsGetFamInstEnvs
+ tracePm "normalise" (ppr typ)
-- Before proceeding, we chuck typ into the constraint solver, in case
-- solving for given equalities may reduce typ some. See
-- "Wrinkle: local equalities" in Note [Type normalisation].
@@ -493,6 +378,30 @@ pmIsClosedType ty
is_algebraic_like :: TyCon -> Bool
is_algebraic_like tc = isAlgTyCon tc || tc == tYPETyCon
+-- | Normalise the given source type to WHNF. If it isn't already in WHNF
+-- ('isSourceTypeInWHNF') , it will normalise the type and then try to step
+-- through type family applications, but not data family applications or
+-- newtypes.
+--
+-- This is a pretty common case of calling 'pmTopNormaliseType' and it should be
+-- efficient.
+normaliseSourceTypeWHNF :: TyState -> Type -> DsM Type
+normaliseSourceTypeWHNF _ ty | isSourceTypeInWHNF ty = pure ty
+normaliseSourceTypeWHNF ty_st ty = do
+ pmTopNormaliseType ty_st ty >>= \case
+ NoChange ty -> pure ty
+ NormalisedByConstraints ty -> pure ty
+ HadRedexes ty _ _ -> pure ty
+
+-- | Is the source type in WHNF wrt. 'pmTopNormaliseType'?
+--
+-- Returns False if the given type is not a TyCon application, or if the TyCon
+-- app head is a type family TyCon. (But not for data family TyCons!)
+isSourceTypeInWHNF :: Type -> Bool
+isSourceTypeInWHNF ty
+ | Just (tc, _) <- splitTyConApp_maybe ty = not (isTypeFamilyTyCon tc)
+ | otherwise = False
+
{- Note [Type normalisation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Constructs like -XEmptyCase or a previous unsuccessful pattern match on a data
@@ -568,160 +477,6 @@ pmTopNormaliseType, using the constraint solver to solve for any local
equalities (such as i ~ Int) that may be in scope.
-}
-----------------
--- * Type oracle
-
--- | Allocates a fresh 'EvVar' name for 'PredTy's.
-nameTyCt :: PredType -> DsM EvVar
-nameTyCt pred_ty = do
- unique <- getUniqueM
- let occname = mkVarOccFS (fsLit ("pm_"++show unique))
- idname = mkInternalName unique occname noSrcSpan
- return (mkLocalIdOrCoVar idname Many pred_ty)
-
--- | Add some extra type constraints to the 'TyState'; return 'Nothing' if we
--- find a contradiction (e.g. @Int ~ Bool@).
-tyOracle :: TyState -> Bag PredType -> DsM (Maybe TyState)
-tyOracle (TySt inert) cts
- = do { evs <- traverse nameTyCt cts
- ; tracePm "tyOracle" (ppr cts)
- ; ((_warns, errs), res) <- initTcDsForSolver $ tcCheckSatisfiability inert evs
- ; case res of
- Just mb_new_inert -> return (TySt <$> mb_new_inert)
- Nothing -> pprPanic "tyOracle" (vcat $ pprErrMsgBagWithLoc errs) }
-
--- | A 'SatisfiabilityCheck' based on new type-level constraints.
--- Returns a new 'Nabla' if the new constraints are compatible with existing
--- ones. Doesn't bother calling out to the type oracle if the bag of new type
--- constraints was empty. Will only recheck 'ResidualCompleteMatches' in the term oracle
--- for emptiness if the first argument is 'True'.
-tyIsSatisfiable :: Bool -> Bag PredType -> SatisfiabilityCheck
-tyIsSatisfiable recheck_complete_sets new_ty_cs = SC $ \nabla ->
- if isEmptyBag new_ty_cs
- then pure (Just nabla)
- else tyOracle (nabla_ty_st nabla) new_ty_cs >>= \case
- Nothing -> pure Nothing
- Just ty_st' -> do
- let nabla' = nabla{ nabla_ty_st = ty_st' }
- if recheck_complete_sets
- then ensureAllInhabited nabla'
- else pure (Just nabla')
-
-
-{- *********************************************************************
-* *
- DIdEnv with sharing
-* *
-********************************************************************* -}
-
-
-{- *********************************************************************
-* *
- TmState
- What we know about terms
-* *
-********************************************************************* -}
-
-{- Note [The Pos/Neg invariant]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Invariant applying to each VarInfo: Whenever we have @(C, [y,z])@ in 'vi_pos',
-any entry in 'vi_neg' must be incomparable to C (return Nothing) according to
-'eqPmAltCons'. Those entries that are comparable either lead to a refutation
-or are redundant. Examples:
-* @x ~ Just y@, @x ≁ [Just]@. 'eqPmAltCon' returns @Equal@, so refute.
-* @x ~ Nothing@, @x ≁ [Just]@. 'eqPmAltCon' returns @Disjoint@, so negative
- info is redundant and should be discarded.
-* @x ~ I# y@, @x ≁ [4,2]@. 'eqPmAltCon' returns @PossiblyOverlap@, so orthogal.
- We keep this info in order to be able to refute a redundant match on i.e. 4
- later on.
-
-This carries over to pattern synonyms and overloaded literals. Say, we have
- pattern Just42 = Just 42
- case Just42 of x
- Nothing -> ()
- Just _ -> ()
-Even though we had a solution for the value abstraction called x here in form
-of a PatSynCon (Just42,[]), this solution is incomparable to both Nothing and
-Just. Hence we retain the info in vi_neg, which eventually allows us to detect
-the complete pattern match.
-
-The Pos/Neg invariant extends to vi_rcm, which stores essentially positive
-information. We make sure that vi_neg and vi_rcm never overlap. This isn't
-strictly necessary since vi_rcm is just a cache, so doesn't need to be
-accurate: Every suggestion of a possible ConLike from vi_rcm might be
-refutable by the type oracle anyway. But it helps to maintain sanity while
-debugging traces.
-
-Note [Why record both positive and negative info?]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-You might think that knowing positive info (like x ~ Just y) would render
-negative info irrelevant, but not so because of pattern synonyms. E.g we might
-know that x cannot match (Foo 4), where pattern Foo p = Just p
-
-Also overloaded literals themselves behave like pattern synonyms. E.g if
-postively we know that (x ~ I# y), we might also negatively want to record that
-x does not match 45 f 45 = e2 f (I# 22#) = e3 f 45 = e4 --
-Overlapped
-
-Note [TmState invariants]
-~~~~~~~~~~~~~~~~~~~~~~~~~
-The term oracle state is never obviously (i.e., without consulting the type
-oracle) contradictory. This implies a few invariants:
-* Whenever vi_pos overlaps with vi_neg according to 'eqPmAltCon', we refute.
- This is implied by the Note [Pos/Neg invariant].
-* Whenever vi_neg subsumes a COMPLETE set, we refute. We consult vi_rcm to
- detect this, but we could just compare whole COMPLETE sets to vi_neg every
- time, if it weren't for performance.
-
-Maintaining these invariants in 'addVarCt' (the core of the term oracle) and
-'addNotConCt' is subtle.
-* Merging VarInfos. Example: Add the fact @x ~ y@ (see 'equate').
- - (COMPLETE) If we had @x ≁ True@ and @y ≁ False@, then we get
- @x ≁ [True,False]@. This is vacuous by matter of comparing to the built-in
- COMPLETE set, so should refute.
- - (Pos/Neg) If we had @x ≁ True@ and @y ~ True@, we have to refute.
-* Adding positive information. Example: Add the fact @x ~ K ys@ (see 'addConCt')
- - (Neg) If we had @x ≁ K@, refute.
- - (Pos) If we had @x ~ K2@, and that contradicts the new solution according to
- 'eqPmAltCon' (ex. K2 is [] and K is (:)), then refute.
- - (Refine) If we had @x ≁ K zs@, unify each y with each z in turn.
-* Adding negative information. Example: Add the fact @x ≁ Nothing@ (see 'addNotConCt')
- - (Refut) If we have @x ~ K ys@, refute.
- - (COMPLETE) If K=Nothing and we had @x ≁ Just@, then we get
- @x ≁ [Just,Nothing]@. This is vacuous by matter of comparing to the built-in
- COMPLETE set, so should refute.
-
-Note that merging VarInfo in equate can be done by calling out to 'addConCt' and
-'addNotConCt' for each of the facts individually.
-
-Note [Representation of Strings in TmState]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Instead of treating regular String literals as a PmLits, we treat it as a list
-of characters in the oracle for better overlap reasoning. The following example
-shows why:
-
- f :: String -> ()
- f ('f':_) = ()
- f "foo" = ()
- f _ = ()
-
-The second case is redundant, and we like to warn about it. Therefore either
-the oracle will have to do some smart conversion between the list and literal
-representation or treat is as the list it really is at runtime.
-
-The "smart conversion" has the advantage of leveraging the more compact literal
-representation wherever possible, but is really nasty to get right with negative
-equalities: Just think of how to encode @x /= "foo"@.
-The "list" option is far simpler, but incurs some overhead in representation and
-warning messages (which can be alleviated by someone with enough dedication).
--}
-
--- | A 'SatisfiabilityCheck' based on new term-level constraints.
--- Returns a new 'Nabla' if the new constraints are compatible with existing
--- ones.
-tmIsSatisfiable :: Bag TmCt -> SatisfiabilityCheck
-tmIsSatisfiable new_tm_cs = SC $ \nabla -> runMaybeT $ foldlM addTmCt nabla new_tm_cs
-
-----------------------
-- * Looking up VarInfo
@@ -729,16 +484,19 @@ emptyRCM :: ResidualCompleteMatches
emptyRCM = RCM Nothing Nothing
emptyVarInfo :: Id -> VarInfo
--- We could initialise @bot@ to @Just False@ in case of an unlifted type here,
--- but it's cleaner to let the user of the constraint solver take care of this.
--- After all, there are also strict fields, the unliftedness of which isn't
--- evident in the type. So treating unlifted types here would never be
--- sufficient anyway.
-emptyVarInfo x = VI (idType x) [] emptyPmAltConSet MaybeBot emptyRCM
+emptyVarInfo x
+ = VI
+ { vi_id = x
+ , vi_pos = []
+ , vi_neg = emptyPmAltConSet
+ -- Case (3) in Note [Strict fields and fields of unlifted type]
+ , vi_bot = if isUnliftedType (idType x) then IsNotBot else MaybeBot
+ , vi_rcm = emptyRCM
+ }
lookupVarInfo :: TmState -> Id -> VarInfo
-- (lookupVarInfo tms x) tells what we know about 'x'
-lookupVarInfo (TmSt env _) x = fromMaybe (emptyVarInfo x) (lookupSDIE env x)
+lookupVarInfo (TmSt env _ _) x = fromMaybe (emptyVarInfo x) (lookupSDIE env x)
-- | Like @lookupVarInfo ts x@, but @lookupVarInfo ts x = (y, vi)@ also looks
-- through newtype constructors. We have @x ~ N1 (... (Nk y))@ such that the
@@ -756,10 +514,17 @@ lookupVarInfoNT ts x = case lookupVarInfo ts x of
res -> (x, res)
where
as_newtype = listToMaybe . mapMaybe go
- go (PmAltConLike (RealDataCon dc), _, [y])
+ go PACA{paca_con = PmAltConLike (RealDataCon dc), paca_ids = [y]}
| isNewDataCon dc = Just y
go _ = Nothing
+trvVarInfo :: Functor f => (VarInfo -> f (a, VarInfo)) -> Nabla -> Id -> f (a, Nabla)
+trvVarInfo f nabla@MkNabla{ nabla_tm_st = ts@TmSt{ts_facts = env} } x
+ = set_vi <$> f (lookupVarInfo ts x)
+ where
+ set_vi (a, vi') =
+ (a, nabla{ nabla_tm_st = ts{ ts_facts = setEntrySDIE env (vi_id vi') vi' } })
+
------------------------------------------------
-- * Exported utility functions querying 'Nabla'
@@ -794,156 +559,218 @@ where you can find the solution in a perhaps more digestible format.
lookupRefuts :: Uniquable k => Nabla -> k -> [PmAltCon]
-- Unfortunately we need the extra bit of polymorphism and the unfortunate
-- duplication of lookupVarInfo here.
-lookupRefuts MkNabla{ nabla_tm_st = ts@(TmSt (SDIE env) _) } k =
+lookupRefuts MkNabla{ nabla_tm_st = ts@(TmSt{ts_facts = (SDIE env)}) } k =
case lookupUDFM_Directly env (getUnique k) of
Nothing -> []
Just (Indirect y) -> pmAltConSetElems (vi_neg (lookupVarInfo ts y))
Just (Entry vi) -> pmAltConSetElems (vi_neg vi)
-isDataConSolution :: (PmAltCon, [TyVar], [Id]) -> Bool
-isDataConSolution (PmAltConLike (RealDataCon _), _, _) = True
-isDataConSolution _ = False
+isDataConSolution :: PmAltConApp -> Bool
+isDataConSolution PACA{paca_con = PmAltConLike (RealDataCon _)} = True
+isDataConSolution _ = False
-- @lookupSolution nabla x@ picks a single solution ('vi_pos') of @x@ from
-- possibly many, preferring 'RealDataCon' solutions whenever possible.
-lookupSolution :: Nabla -> Id -> Maybe (PmAltCon, [TyVar], [Id])
+lookupSolution :: Nabla -> Id -> Maybe PmAltConApp
lookupSolution nabla x = case vi_pos (lookupVarInfo (nabla_tm_st nabla) x) of
[] -> Nothing
pos
| Just sol <- find isDataConSolution pos -> Just sol
| otherwise -> Just (head pos)
--------------------------------
--- * Adding facts to the oracle
-
--- | A term constraint.
-data TmCt
- = TmVarCt !Id !Id
- -- ^ @TmVarCt x y@ encodes "x ~ y", equating @x@ and @y@.
- | TmCoreCt !Id !CoreExpr
- -- ^ @TmCoreCt x e@ encodes "x ~ e", equating @x@ with the 'CoreExpr' @e@.
- | TmConCt !Id !PmAltCon ![TyVar] ![Id]
- -- ^ @TmConCt x K tvs ys@ encodes "x ~ K @tvs ys", equating @x@ with the 'PmAltCon'
- -- application @K @tvs ys@.
- | TmNotConCt !Id !PmAltCon
- -- ^ @TmNotConCt x K@ encodes "x ≁ K", asserting that @x@ can't be headed
+-------------------------
+-- * Adding φ constraints
+--
+-- Figure 7 in the LYG paper.
+
+-- | A high-level pattern-match constraint. Corresponds to φ from Figure 3 of
+-- the LYG paper.
+data PhiCt
+ = PhiTyCt !PredType
+ -- ^ A type constraint "T ~ U".
+ | PhiCoreCt !Id !CoreExpr
+ -- ^ @PhiCoreCt x e@ encodes "x ~ e", equating @x@ with the 'CoreExpr' @e@.
+ | PhiConCt !Id !PmAltCon ![TyVar] ![PredType] ![Id]
+ -- ^ @PhiConCt x K tvs dicts ys@ encodes @K \@tvs dicts ys <- x@, matching @x@
+ -- against the 'PmAltCon' application @K \@tvs dicts ys@, binding @tvs@,
+ -- @dicts@ and possibly unlifted fields @ys@ in the process.
+ -- See Note [Strict fields and fields of unlifted type].
+ | PhiNotConCt !Id !PmAltCon
+ -- ^ @PhiNotConCt x K@ encodes "x ≁ K", asserting that @x@ can't be headed
-- by @K@.
- | TmBotCt !Id
- -- ^ @TmBotCt x@ encodes "x ~ ⊥", equating @x@ to ⊥.
+ | PhiBotCt !Id
+ -- ^ @PhiBotCt x@ encodes "x ~ ⊥", equating @x@ to ⊥.
-- by @K@.
- | TmNotBotCt !Id
- -- ^ @TmNotBotCt x y@ encodes "x ≁ ⊥", asserting that @x@ can't be ⊥.
-
-instance Outputable TmCt where
- ppr (TmVarCt x y) = ppr x <+> char '~' <+> ppr y
- ppr (TmCoreCt x e) = ppr x <+> char '~' <+> ppr e
- ppr (TmConCt x con tvs args) = ppr x <+> char '~' <+> hsep (ppr con : pp_tvs ++ pp_args)
+ | PhiNotBotCt !Id
+ -- ^ @PhiNotBotCt x y@ encodes "x ≁ ⊥", asserting that @x@ can't be ⊥.
+
+instance Outputable PhiCt where
+ ppr (PhiTyCt ty_ct) = ppr ty_ct
+ ppr (PhiCoreCt x e) = ppr x <+> char '~' <+> ppr e
+ ppr (PhiConCt x con tvs dicts args) =
+ hsep (ppr con : pp_tvs ++ pp_dicts ++ pp_args) <+> text "<-" <+> ppr x
where
- pp_tvs = map ((<> char '@') . ppr) tvs
- pp_args = map ppr args
- ppr (TmNotConCt x con) = ppr x <+> text "≁" <+> ppr con
- ppr (TmBotCt x) = ppr x <+> text "~ ⊥"
- ppr (TmNotBotCt x) = ppr x <+> text "≁ ⊥"
-
-type TyCt = PredType
-
--- | An oracle constraint.
-data PmCt
- = PmTyCt !TyCt
- -- ^ @PmTy pred_ty@ carries 'PredType's, for example equality constraints.
- | PmTmCt !TmCt
- -- ^ A term constraint.
-
-type PmCts = Bag PmCt
-
-pattern PmVarCt :: Id -> Id -> PmCt
-pattern PmVarCt x y = PmTmCt (TmVarCt x y)
-pattern PmCoreCt :: Id -> CoreExpr -> PmCt
-pattern PmCoreCt x e = PmTmCt (TmCoreCt x e)
-pattern PmConCt :: Id -> PmAltCon -> [TyVar] -> [Id] -> PmCt
-pattern PmConCt x con tvs args = PmTmCt (TmConCt x con tvs args)
-pattern PmNotConCt :: Id -> PmAltCon -> PmCt
-pattern PmNotConCt x con = PmTmCt (TmNotConCt x con)
-pattern PmBotCt :: Id -> PmCt
-pattern PmBotCt x = PmTmCt (TmBotCt x)
-pattern PmNotBotCt :: Id -> PmCt
-pattern PmNotBotCt x = PmTmCt (TmNotBotCt x)
-{-# COMPLETE PmTyCt, PmVarCt, PmCoreCt, PmConCt, PmNotConCt, PmBotCt, PmNotBotCt #-}
-
-instance Outputable PmCt where
- ppr (PmTyCt pred_ty) = ppr pred_ty
- ppr (PmTmCt tm_ct) = ppr tm_ct
+ pp_tvs = map ((<> char '@') . ppr) tvs
+ pp_dicts = map ppr dicts
+ pp_args = map ppr args
+ ppr (PhiNotConCt x con) = ppr x <+> text "≁" <+> ppr con
+ ppr (PhiBotCt x) = ppr x <+> text "~ ⊥"
+ ppr (PhiNotBotCt x) = ppr x <+> text "≁ ⊥"
+
+type PhiCts = Bag PhiCt
+
+-- | The fuel for the inhabitation test.
+-- See Note [Fuel for the inhabitation test].
+initFuel :: Int
+initFuel = 4 -- 4 because it's the smallest number that passes f' in T17977b
-- | Adds new constraints to 'Nabla' and returns 'Nothing' if that leads to a
-- contradiction.
-addPmCts :: Nabla -> PmCts -> DsM (Maybe Nabla)
+--
+-- In terms of the paper, this function models the \(⊕_φ\) function in
+-- Figure 7 on batches of φ constraints.
+addPhiCts :: Nabla -> PhiCts -> DsM (Maybe Nabla)
-- See Note [TmState invariants].
-addPmCts nabla cts = do
- let (ty_cts, tm_cts) = partitionTyTmCts cts
- runSatisfiabilityCheck nabla $ mconcat
- [ tyIsSatisfiable True (listToBag ty_cts)
- , tmIsSatisfiable (listToBag tm_cts)
- ]
-
-partitionTyTmCts :: PmCts -> ([TyCt], [TmCt])
-partitionTyTmCts = partitionEithers . map to_either . toList
+addPhiCts nabla cts = runMaybeT $ do
+ let (ty_cts, tm_cts) = partitionPhiCts cts
+ nabla' <- addTyCts nabla (listToBag ty_cts)
+ nabla'' <- foldlM addPhiTmCt nabla' (listToBag tm_cts)
+ inhabitationTest initFuel (nabla_ty_st nabla) nabla''
+
+partitionPhiCts :: PhiCts -> ([PredType], [PhiCt])
+partitionPhiCts = partitionEithers . map to_either . toList
where
- to_either (PmTyCt pred_ty) = Left pred_ty
- to_either (PmTmCt tm_ct) = Right tm_ct
-
--- | Adds a single term constraint by dispatching to the various term oracle
--- functions.
-addTmCt :: Nabla -> TmCt -> MaybeT DsM Nabla
-addTmCt nabla (TmVarCt x y) = addVarCt nabla x y
-addTmCt nabla (TmCoreCt x e) = addCoreCt nabla x e
-addTmCt nabla (TmConCt x con tvs args) = addConCt nabla x con tvs args
-addTmCt nabla (TmNotConCt x con) = addNotConCt nabla x con
-addTmCt nabla (TmBotCt x) = addBotCt nabla x
-addTmCt nabla (TmNotBotCt x) = addNotBotCt nabla x
+ to_either (PhiTyCt pred_ty) = Left pred_ty
+ to_either ct = Right ct
+
+-----------------------------
+-- ** Adding type constraints
+
+-- | Adds new type-level constraints by calling out to the type-checker via
+-- 'tyOracle'.
+addTyCts :: Nabla -> Bag PredType -> MaybeT DsM Nabla
+addTyCts nabla@MkNabla{ nabla_ty_st = ty_st } new_ty_cs = do
+ ty_st' <- MaybeT (tyOracle ty_st new_ty_cs)
+ pure nabla{ nabla_ty_st = ty_st' }
+
+-- | Add some extra type constraints to the 'TyState'; return 'Nothing' if we
+-- find a contradiction (e.g. @Int ~ Bool@).
+tyOracle :: TyState -> Bag PredType -> DsM (Maybe TyState)
+tyOracle ty_st@(TySt n inert) cts
+ | isEmptyBag cts
+ = pure (Just ty_st)
+ | otherwise
+ = do { evs <- traverse nameTyCt cts
+ ; tracePm "tyOracle" (ppr cts $$ ppr inert)
+ ; ((_warns, errs), res) <- initTcDsForSolver $ tcCheckSatisfiability inert evs
+ ; case res of
+ -- return the new inert set and increment the sequence number n
+ Just mb_new_inert -> return (TySt (n+1) <$> mb_new_inert)
+ Nothing -> pprPanic "tyOracle" (vcat $ pprErrMsgBagWithLoc errs) }
+
+-- | Allocates a fresh 'EvVar' name for 'PredTy's.
+nameTyCt :: PredType -> DsM EvVar
+nameTyCt pred_ty = do
+ unique <- getUniqueM
+ let occname = mkVarOccFS (fsLit ("pm_"++show unique))
+ idname = mkInternalName unique occname noSrcSpan
+ return (mkLocalIdOrCoVar idname Many pred_ty)
+
+-----------------------------
+-- ** Adding term constraints
+
+-- | Adds a single higher-level φ constraint by dispatching to the various
+-- oracle functions.
+--
+-- In terms of the paper, this function amounts to the constructor constraint
+-- case of \(⊕_φ\) in Figure 7, which "desugars" higher-level φ constraints
+-- into lower-level δ constraints. We don't have a data type for δ constraints
+-- and call the corresponding oracle function directly instead.
+--
+-- Precondition: The φ is /not/ a type constraint! These should be handled by
+-- 'addTyCts' before, through 'addPhiCts'.
+addPhiTmCt :: Nabla -> PhiCt -> MaybeT DsM Nabla
+addPhiTmCt _ (PhiTyCt ct) = pprPanic "addPhiCt:TyCt" (ppr ct) -- See the precondition
+addPhiTmCt nabla (PhiCoreCt x e) = addCoreCt nabla x e
+addPhiTmCt nabla (PhiConCt x con tvs dicts args) = do
+ -- Case (1) of Note [Strict fields and variables of unlifted type]
+ -- PhiConCt correspond to the higher-level φ constraints from the paper with
+ -- bindings semantics. It disperses into lower-level δ constraints that the
+ -- 'add*Ct' functions correspond to.
+ nabla' <- addTyCts nabla (listToBag dicts)
+ nabla'' <- addConCt nabla' x con tvs args
+ foldlM addNotBotCt nabla'' (filterUnliftedFields con args)
+addPhiTmCt nabla (PhiNotConCt x con) = addNotConCt nabla x con
+addPhiTmCt nabla (PhiBotCt x) = addBotCt nabla x
+addPhiTmCt nabla (PhiNotBotCt x) = addNotBotCt nabla x
+
+filterUnliftedFields :: PmAltCon -> [Id] -> [Id]
+filterUnliftedFields con args =
+ [ arg | (arg, bang) <- zipEqual "addPhiCt" args (pmAltConImplBangs con)
+ , isBanged bang || isUnliftedType (idType arg) ]
-- | Adds the constraint @x ~ ⊥@, e.g. that evaluation of a particular 'Id' @x@
-- surely diverges. Quite similar to 'addConCt', only that it only cares about
-- ⊥.
addBotCt :: Nabla -> Id -> MaybeT DsM Nabla
-addBotCt nabla@MkNabla{ nabla_tm_st = TmSt env reps } x = do
+addBotCt nabla@MkNabla{ nabla_tm_st = ts@TmSt{ ts_facts=env } } x = do
let (y, vi@VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x
case bot of
IsNotBot -> mzero -- There was x ≁ ⊥. Contradiction!
IsBot -> pure nabla -- There already is x ~ ⊥. Nothing left to do
MaybeBot -> do -- We add x ~ ⊥
let vi' = vi{ vi_bot = IsBot }
- pure nabla{ nabla_tm_st = TmSt (setEntrySDIE env y vi') reps}
+ pure nabla{ nabla_tm_st = ts{ts_facts = setEntrySDIE env y vi' } }
+
+-- | Adds the constraint @x ~/ ⊥@ to 'Nabla'. Quite similar to 'addNotConCt',
+-- but only cares for the ⊥ "constructor".
+addNotBotCt :: Nabla -> Id -> MaybeT DsM Nabla
+addNotBotCt nabla@MkNabla{ nabla_tm_st = ts@TmSt{ts_facts=env} } x = do
+ let (y, vi@VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x
+ case bot of
+ IsBot -> mzero -- There was x ~ ⊥. Contradiction!
+ IsNotBot -> pure nabla -- There already is x ≁ ⊥. Nothing left to do
+ MaybeBot -> do -- We add x ≁ ⊥ and test if x is still inhabited
+ -- Mark dirty for a delayed inhabitation test
+ let vi' = vi{ vi_bot = IsNotBot}
+ pure $ markDirty y
+ $ nabla{ nabla_tm_st = ts{ ts_facts = setEntrySDIE env y vi' } }
-- | Record a @x ~/ K@ constraint, e.g. that a particular 'Id' @x@ can't
-- take the shape of a 'PmAltCon' @K@ in the 'Nabla' and return @Nothing@ if
-- that leads to a contradiction.
-- See Note [TmState invariants].
addNotConCt :: Nabla -> Id -> PmAltCon -> MaybeT DsM Nabla
-addNotConCt _ _ (PmAltConLike (RealDataCon dc))
+addNotConCt _ _ (PmAltConLike (RealDataCon dc))
| isNewDataCon dc = mzero -- (3) in Note [Coverage checking Newtype matches]
-addNotConCt nabla@MkNabla{ nabla_tm_st = ts@(TmSt env reps) } x nalt = do
- let vi@(VI _ pos neg _ rcm) = lookupVarInfo ts x
- -- 1. Bail out quickly when nalt contradicts a solution
- let contradicts nalt (cl, _tvs, _args) = eqPmAltCon cl nalt == Equal
- guard (not (any (contradicts nalt) pos))
- -- 2. Only record the new fact when it's not already implied by one of the
- -- solutions
- let implies nalt (cl, _tvs, _args) = eqPmAltCon cl nalt == Disjoint
- let neg'
- | any (implies nalt) pos = neg
- -- See Note [Completeness checking with required Thetas]
- | hasRequiredTheta nalt = neg
- | otherwise = extendPmAltConSet neg nalt
- MASSERT( isPmAltConMatchStrict nalt )
- let vi1 = vi{ vi_neg = neg', vi_bot = IsNotBot }
- -- 3. Make sure there's at least one other possible constructor
- vi2 <- case nalt of
- PmAltConLike cl -> do
- rcm' <- lift (markMatched cl rcm)
- ensureInhabited nabla vi1{ vi_rcm = rcm' }
- _ ->
- pure vi1
- pure nabla{ nabla_tm_st = TmSt (setEntrySDIE env x vi2) reps }
+addNotConCt nabla x nalt = do
+ (mb_mark_dirty, nabla') <- trvVarInfo go nabla x
+ pure $ case mb_mark_dirty of
+ Just x -> markDirty x nabla'
+ Nothing -> nabla'
+ where
+ go vi@(VI x' pos neg _ rcm) = do
+ -- 1. Bail out quickly when nalt contradicts a solution
+ let contradicts nalt sol = eqPmAltCon (paca_con sol) nalt == Equal
+ guard (not (any (contradicts nalt) pos))
+ -- 2. Only record the new fact when it's not already implied by one of the
+ -- solutions
+ let implies nalt sol = eqPmAltCon (paca_con sol) nalt == Disjoint
+ let neg'
+ | any (implies nalt) pos = neg
+ -- See Note [Completeness checking with required Thetas]
+ | hasRequiredTheta nalt = neg
+ | otherwise = extendPmAltConSet neg nalt
+ MASSERT( isPmAltConMatchStrict nalt )
+ let vi' = vi{ vi_neg = neg', vi_bot = IsNotBot }
+ -- 3. Make sure there's at least one other possible constructor
+ case nalt of
+ PmAltConLike cl -> do
+ -- Mark dirty to force a delayed inhabitation test
+ rcm' <- lift (markMatched cl rcm)
+ pure (Just x', vi'{ vi_rcm = rcm' })
+ _ ->
+ pure (Nothing, vi')
hasRequiredTheta :: PmAltCon -> Bool
hasRequiredTheta (PmAltConLike cl) = notNull req_theta
@@ -951,159 +778,54 @@ hasRequiredTheta (PmAltConLike cl) = notNull req_theta
(_,_,_,_,req_theta,_,_) = conLikeFullSig cl
hasRequiredTheta _ = False
-{- Note [Completeness checking with required Thetas]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Consider the situation in #11224
-
- import Text.Read (readMaybe)
- pattern PRead :: Read a => () => a -> String
- pattern PRead x <- (readMaybe -> Just x)
- f :: String -> Int
- f (PRead x) = x
- f (PRead xs) = length xs
- f _ = 0
-
-Is the first match exhaustive on the PRead synonym? Should the second line thus
-deemed redundant? The answer is, of course, No! The required theta is like a
-hidden parameter which must be supplied at the pattern match site, so PRead
-is much more like a view pattern (where behavior depends on the particular value
-passed in).
-The simple solution here is to forget in 'addNotConCt' that we matched
-on synonyms with a required Theta like @PRead@, so that subsequent matches on
-the same constructor are never flagged as redundant. The consequence is that
-we no longer detect the actually redundant match in
-
- g :: String -> Int
- g (PRead x) = x
- g (PRead y) = y -- redundant!
- g _ = 0
-
-But that's a small price to pay, compared to the proper solution here involving
-storing required arguments along with the PmAltConLike in 'vi_neg'.
--}
-
--- | Guess the universal argument types of a ConLike from an instantiation of
--- its result type. Rather easy for DataCons, but not so much for PatSynCons.
--- See Note [Pattern synonym result type] in "GHC.Core.PatSyn".
-guessConLikeUnivTyArgsFromResTy :: FamInstEnvs -> Type -> ConLike -> Maybe [Type]
-guessConLikeUnivTyArgsFromResTy env res_ty (RealDataCon dc) = do
- (tc, tc_args) <- splitTyConApp_maybe res_ty
- -- Consider data families: In case of a DataCon, we need to translate to
- -- the representation TyCon. For PatSyns, they are relative to the data
- -- family TyCon, so we don't need to translate them.
- let (rep_tc, tc_args', _) = tcLookupDataFamInst env tc tc_args
- if rep_tc == dataConTyCon dc
- then Just tc_args'
- else Nothing
-guessConLikeUnivTyArgsFromResTy _ res_ty (PatSynCon ps) = do
- -- We are successful if we managed to instantiate *every* univ_tv of con.
- -- This is difficult and bound to fail in some cases, see
- -- Note [Pattern synonym result type] in GHC.Core.PatSyn. So we just try our best
- -- here and be sure to return an instantiation when we can substitute every
- -- universally quantified type variable.
- -- We *could* instantiate all the other univ_tvs just to fresh variables, I
- -- suppose, but that means we get weird field types for which we don't know
- -- anything. So we prefer to keep it simple here.
- let (univ_tvs,_,_,_,_,con_res_ty) = patSynSig ps
- subst <- tcMatchTy con_res_ty res_ty
- traverse (lookupTyVar subst) univ_tvs
-
--- | Adds the constraint @x ~/ ⊥@ to 'Nabla'. Quite similar to 'addNotConCt',
--- but only cares for the ⊥ "constructor".
-addNotBotCt :: Nabla -> Id -> MaybeT DsM Nabla
-addNotBotCt nabla@MkNabla{ nabla_tm_st = TmSt env reps } x = do
- let (y, vi@VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x
- case bot of
- IsBot -> mzero -- There was x ~ ⊥. Contradiction!
- IsNotBot -> pure nabla -- There already is x ≁ ⊥. Nothing left to do
- MaybeBot -> do -- We add x ≁ ⊥ and test if x is still inhabited
- vi <- ensureInhabited nabla vi{ vi_bot = IsNotBot }
- pure nabla{ nabla_tm_st = TmSt (setEntrySDIE env y vi) reps}
-
--- | Returns (Just vi) if at least one member of each ConLike in the COMPLETE
--- set satisfies the oracle
---
--- Internally uses and updates the ConLikeSets in vi_rcm.
+-- | Add a @x ~ K tvs args ts@ constraint.
+-- @addConCt x K tvs args ts@ extends the substitution with a solution
+-- @x :-> (K, tvs, args)@ if compatible with the negative and positive info we
+-- have on @x@, reject (@Nothing@) otherwise.
--
--- NB: Does /not/ filter each ConLikeSet with the oracle; members may
--- remain that do not statisfy it. This lazy approach just
--- avoids doing unnecessary work.
-ensureInhabited :: Nabla -> VarInfo -> MaybeT DsM VarInfo
-ensureInhabited nabla vi = case vi_bot vi of
- MaybeBot -> pure vi -- The |-Bot rule from the paper
- IsBot -> pure vi
- IsNotBot -> lift (add_matches vi) >>= inst_complete_sets
- where
- add_matches :: VarInfo -> DsM VarInfo
- add_matches vi = do
- res <- pmTopNormaliseType (nabla_ty_st nabla) (vi_ty vi)
- rcm <- case reprTyCon_maybe (normalisedSourceType res) of
- Just tc -> addTyConMatches tc (vi_rcm vi)
- Nothing -> addCompleteMatches (vi_rcm vi)
- pure vi{ vi_rcm = rcm }
-
- reprTyCon_maybe :: Type -> Maybe TyCon
- reprTyCon_maybe ty = case splitTyConApp_maybe ty of
- Nothing -> Nothing
- Just (tc, _args) -> case tyConFamInst_maybe tc of
- Nothing -> Just tc
- Just (tc_fam, _) -> Just tc_fam
-
- -- | This is the |-Inst rule from the paper (section 4.5). Tries to
- -- find an inhabitant in every complete set by instantiating with one their
- -- constructors. If there is any complete set where we can't find an
- -- inhabitant, the whole thing is uninhabited.
- -- See also Note [Implementation of COMPLETE pragmas].
- inst_complete_sets :: VarInfo -> MaybeT DsM VarInfo
- inst_complete_sets vi@VI{ vi_rcm = rcm } = do
- rcm' <- trvRcm (\cls -> inst_complete_set vi cls (uniqDSetToList cls)) rcm
- pure vi{ vi_rcm = rcm' }
-
- inst_complete_set :: VarInfo -> ConLikeSet -> [ConLike] -> MaybeT DsM ConLikeSet
- -- (inst_complete_set cs cls) iterates over cls, deleting from cs
- -- any uninhabited elements of cls. Stop (returning Just cs)
- -- when you see an inhabited element; return Nothing if all
- -- are uninhabited
- inst_complete_set _ _ [] = mzero
- inst_complete_set vi cs (con:cons) = lift (inst_and_test vi con) >>= \case
- True -> pure cs
- False -> inst_complete_set vi (delOneFromUniqDSet cs con) cons
-
- inst_and_test :: VarInfo -> ConLike -> DsM Bool
- -- @inst_and_test K@ Returns False if a non-bottom value @v::ty@ cannot possibly
- -- be of form @K _ _ _@. Returning True is always sound.
- --
- -- It's like 'DataCon.dataConCannotMatch', but more clever because it takes
- -- the facts in Nabla into account.
- inst_and_test vi con = do
- env <- dsGetFamInstEnvs
- case guessConLikeUnivTyArgsFromResTy env (vi_ty vi) con of
- Nothing -> pure True -- be conservative about this
- Just arg_tys -> do
- (_tvs, _vars, ty_cs, strict_arg_tys) <- mkOneConFull arg_tys con
- tracePm "inst_and_test" (ppr con $$ ppr ty_cs)
- -- No need to run the term oracle compared to pmIsSatisfiable
- fmap isJust <$> runSatisfiabilityCheck nabla $ mconcat
- -- Important to pass False to tyIsSatisfiable here, so that we won't
- -- recursively call ensureAllInhabited, leading to an
- -- endless recursion.
- [ tyIsSatisfiable False ty_cs
- , tysAreNonVoid initRecTc strict_arg_tys
- ]
-
--- | Checks if every 'VarInfo' in the term oracle has still an inhabited
--- 'vi_rcm', considering the current type information in 'Nabla'.
--- This check is necessary after having matched on a GADT con to weed out
--- impossible matches.
-ensureAllInhabited :: Nabla -> DsM (Maybe Nabla)
-ensureAllInhabited nabla@MkNabla{ nabla_tm_st = TmSt env reps }
- = runMaybeT (set_tm_cs_env nabla <$> traverseSDIE go env)
- where
- set_tm_cs_env nabla env = nabla{ nabla_tm_st = TmSt env reps }
- go vi = ensureInhabited nabla vi
+-- See Note [TmState invariants].
+addConCt :: Nabla -> Id -> PmAltCon -> [TyVar] -> [Id] -> MaybeT DsM Nabla
+addConCt nabla@MkNabla{ nabla_tm_st = ts@TmSt{ ts_facts=env } } x alt tvs args = do
+ let vi@(VI _ pos neg bot _) = lookupVarInfo ts x
+ -- First try to refute with a negative fact
+ guard (not (elemPmAltConSet alt neg))
+ -- Then see if any of the other solutions (remember: each of them is an
+ -- additional refinement of the possible values x could take) indicate a
+ -- contradiction
+ guard (all ((/= Disjoint) . eqPmAltCon alt . paca_con) pos)
+ -- Now we should be good! Add (alt, tvs, args) as a possible solution, or
+ -- refine an existing one
+ case find ((== Equal) . eqPmAltCon alt . paca_con) pos of
+ Just (PACA _con other_tvs other_args) -> do
+ -- We must unify existentially bound ty vars and arguments!
+ let ty_cts = equateTys (map mkTyVarTy tvs) (map mkTyVarTy other_tvs)
+ when (length args /= length other_args) $
+ lift $ tracePm "error" (ppr x <+> ppr alt <+> ppr args <+> ppr other_args)
+ nabla' <- MaybeT $ addPhiCts nabla (listToBag ty_cts)
+ let add_var_ct nabla (a, b) = addVarCt nabla a b
+ foldlM add_var_ct nabla' $ zipEqual "addConCt" args other_args
+ Nothing -> do
+ let pos' = PACA alt tvs args : pos
+ let nabla_with bot' =
+ nabla{ nabla_tm_st = ts{ts_facts = setEntrySDIE env x (vi{vi_pos = pos', vi_bot = bot'})} }
+ -- Do (2) in Note [Coverage checking Newtype matches]
+ case (alt, args) of
+ (PmAltConLike (RealDataCon dc), [y]) | isNewDataCon dc ->
+ case bot of
+ MaybeBot -> pure (nabla_with MaybeBot)
+ IsBot -> addBotCt (nabla_with MaybeBot) y
+ IsNotBot -> addNotBotCt (nabla_with MaybeBot) y
+ _ -> ASSERT( isPmAltConMatchStrict alt )
+ pure (nabla_with IsNotBot) -- strict match ==> not ⊥
---------------------------------------
--- * Term oracle unification procedure
+equateTys :: [Type] -> [Type] -> [PhiCt]
+equateTys ts us =
+ [ PhiTyCt (mkPrimEqPred t u)
+ | (t, u) <- zipEqual "equateTys" ts us
+ -- The following line filters out trivial Refl constraints, so that we don't
+ -- need to initialise the type oracle that often
+ , not (eqType t u)
+ ]
-- | Adds a @x ~ y@ constraint by trying to unify two 'Id's and record the
-- gained knowledge in 'Nabla'.
@@ -1114,7 +836,7 @@ ensureAllInhabited nabla@MkNabla{ nabla_tm_st = TmSt env reps }
--
-- See Note [TmState invariants].
addVarCt :: Nabla -> Id -> Id -> MaybeT DsM Nabla
-addVarCt nabla@MkNabla{ nabla_tm_st = TmSt env _ } x y
+addVarCt nabla@MkNabla{ nabla_tm_st = TmSt{ ts_facts = env } } x y
-- It's important that we never @equate@ two variables of the same equivalence
-- class, otherwise we might get cyclic substitutions.
-- Cf. 'extendSubstAndSolve' and
@@ -1130,23 +852,23 @@ addVarCt nabla@MkNabla{ nabla_tm_st = TmSt env _ } x y
--
-- See Note [TmState invariants].
equate :: Nabla -> Id -> Id -> MaybeT DsM Nabla
-equate nabla@MkNabla{ nabla_tm_st = TmSt env reps } x y
+equate nabla@MkNabla{ nabla_tm_st = ts@TmSt{ts_facts = env} } x y
= ASSERT( not (sameRepresentativeSDIE env x y) )
case (lookupSDIE env x, lookupSDIE env y) of
- (Nothing, _) -> pure (nabla{ nabla_tm_st = TmSt (setIndirectSDIE env x y) reps })
- (_, Nothing) -> pure (nabla{ nabla_tm_st = TmSt (setIndirectSDIE env y x) reps })
+ (Nothing, _) -> pure (nabla{ nabla_tm_st = ts{ ts_facts = setIndirectSDIE env x y } })
+ (_, Nothing) -> pure (nabla{ nabla_tm_st = ts{ ts_facts = setIndirectSDIE env y x } })
-- Merge the info we have for x into the info for y
(Just vi_x, Just vi_y) -> do
-- This assert will probably trigger at some point...
-- We should decide how to break the tie
- MASSERT2( vi_ty vi_x `eqType` vi_ty vi_y, text "Not same type" )
+ MASSERT2( idType (vi_id vi_x) `eqType` idType (vi_id vi_y), text "Not same type" )
-- First assume that x and y are in the same equivalence class
let env_ind = setIndirectSDIE env x y
-- Then sum up the refinement counters
let env_refs = setEntrySDIE env_ind y vi_y
- let nabla_refs = nabla{ nabla_tm_st = TmSt env_refs reps }
+ let nabla_refs = nabla{ nabla_tm_st = ts{ts_facts = env_refs} }
-- and then gradually merge every positive fact we have on x into y
- let add_fact nabla (cl, tvs, args) = addConCt nabla y cl tvs args
+ let add_fact nabla (PACA cl tvs args) = addConCt nabla y cl tvs args
nabla_pos <- foldlM add_fact nabla_refs (vi_pos vi_x)
-- Do the same for negative info
let add_refut nabla nalt = addNotConCt nabla y nalt
@@ -1155,549 +877,6 @@ equate nabla@MkNabla{ nabla_tm_st = TmSt env reps } x y
-- go!
pure nabla_neg
--- | Add a @x ~ K tvs args ts@ constraint.
--- @addConCt x K tvs args ts@ extends the substitution with a solution
--- @x :-> (K, tvs, args)@ if compatible with the negative and positive info we
--- have on @x@, reject (@Nothing@) otherwise.
---
--- See Note [TmState invariants].
-addConCt :: Nabla -> Id -> PmAltCon -> [TyVar] -> [Id] -> MaybeT DsM Nabla
-addConCt nabla@MkNabla{ nabla_tm_st = ts@(TmSt env reps) } x alt tvs args = do
- let VI ty pos neg bot rcm = lookupVarInfo ts x
- -- First try to refute with a negative fact
- guard (not (elemPmAltConSet alt neg))
- -- Then see if any of the other solutions (remember: each of them is an
- -- additional refinement of the possible values x could take) indicate a
- -- contradiction
- guard (all ((/= Disjoint) . eqPmAltCon alt . fstOf3) pos)
- -- Now we should be good! Add (alt, tvs, args) as a possible solution, or
- -- refine an existing one
- case find ((== Equal) . eqPmAltCon alt . fstOf3) pos of
- Just (_con, other_tvs, other_args) -> do
- -- We must unify existentially bound ty vars and arguments!
- let ty_cts = equateTys (map mkTyVarTy tvs) (map mkTyVarTy other_tvs)
- when (length args /= length other_args) $
- lift $ tracePm "error" (ppr x <+> ppr alt <+> ppr args <+> ppr other_args)
- let tm_cts = zipWithEqual "addConCt" PmVarCt args other_args
- MaybeT $ addPmCts nabla (listToBag ty_cts `unionBags` listToBag tm_cts)
- Nothing -> do
- let pos' = (alt, tvs, args):pos
- let nabla_with bot =
- nabla{ nabla_tm_st = TmSt (setEntrySDIE env x (VI ty pos' neg bot rcm)) reps}
- -- Do (2) in Note [Coverage checking Newtype matches]
- case (alt, args) of
- (PmAltConLike (RealDataCon dc), [y]) | isNewDataCon dc ->
- case bot of
- MaybeBot -> pure (nabla_with MaybeBot)
- IsBot -> addBotCt (nabla_with MaybeBot) y
- IsNotBot -> addNotBotCt (nabla_with MaybeBot) y
- _ -> ASSERT( isPmAltConMatchStrict alt )
- pure (nabla_with IsNotBot) -- strict match ==> not ⊥
-
-equateTys :: [Type] -> [Type] -> [PmCt]
-equateTys ts us =
- [ PmTyCt (mkPrimEqPred t u)
- | (t, u) <- zipEqual "equateTys" ts us
- -- The following line filters out trivial Refl constraints, so that we don't
- -- need to initialise the type oracle that often
- , not (eqType t u)
- ]
-
-----------------------------------------
--- * Enumerating inhabitation candidates
-
--- | Information about a conlike that is relevant to coverage checking.
--- It is called an \"inhabitation candidate\" since it is a value which may
--- possibly inhabit some type, but only if its term constraints ('ic_tm_cs')
--- and type constraints ('ic_ty_cs') are permitting, and if all of its strict
--- argument types ('ic_strict_arg_tys') are inhabitable.
--- See @Note [Strict argument type constraints]@.
-data InhabitationCandidate =
- InhabitationCandidate
- { ic_cs :: PmCts
- , ic_strict_arg_tys :: [Type]
- }
-
-instance Outputable InhabitationCandidate where
- ppr (InhabitationCandidate cs strict_arg_tys) =
- text "InhabitationCandidate" <+>
- vcat [ text "ic_cs =" <+> ppr cs
- , text "ic_strict_arg_tys =" <+> ppr strict_arg_tys ]
-
-mkInhabitationCandidate :: Id -> DataCon -> DsM InhabitationCandidate
--- Precondition: idType x is a TyConApp, so that tyConAppArgs in here is safe.
-mkInhabitationCandidate x dc = do
- let cl = RealDataCon dc
- let tc_args = tyConAppArgs (idType x)
- (ty_vars, arg_vars, ty_cs, strict_arg_tys) <- mkOneConFull tc_args cl
- pure InhabitationCandidate
- { ic_cs = PmTyCt <$> ty_cs `snocBag` PmConCt x (PmAltConLike cl) ty_vars arg_vars
- , ic_strict_arg_tys = strict_arg_tys
- }
-
--- | Generate all 'InhabitationCandidate's for a given type. The result is
--- either @'Left' ty@, if the type cannot be reduced to a closed algebraic type
--- (or if it's one trivially inhabited, like 'Int'), or @'Right' candidates@,
--- if it can. In this case, the candidates are the signature of the tycon, each
--- one accompanied by the term- and type- constraints it gives rise to.
--- See also Note [Checking EmptyCase Expressions]
-inhabitationCandidates :: Nabla -> Type
- -> DsM (Either Type (TyCon, Id, [InhabitationCandidate]))
-inhabitationCandidates MkNabla{ nabla_ty_st = ty_st } ty = do
- pmTopNormaliseType ty_st ty >>= \case
- NoChange _ -> alts_to_check ty ty []
- NormalisedByConstraints ty' -> alts_to_check ty' ty' []
- HadRedexes src_ty dcs core_ty -> alts_to_check src_ty core_ty dcs
- where
- build_newtype :: (Type, DataCon, Type) -> Id -> DsM (Id, PmCt)
- build_newtype (ty, dc, _arg_ty) x = do
- -- ty is the type of @dc x@. It's a @dataConTyCon dc@ application.
- y <- mkPmId ty
- -- Newtypes don't have existentials (yet?!), so passing an empty list as
- -- ex_tvs.
- pure (y, PmConCt y (PmAltConLike (RealDataCon dc)) [] [x])
-
- build_newtypes :: Id -> [(Type, DataCon, Type)] -> DsM (Id, [PmCt])
- build_newtypes x = foldrM (\dc (x, cts) -> go dc x cts) (x, [])
- where
- go dc x cts = second (:cts) <$> build_newtype dc x
-
- -- Inhabitation candidates, using the result of pmTopNormaliseType
- alts_to_check :: Type -> Type -> [(Type, DataCon, Type)]
- -> DsM (Either Type (TyCon, Id, [InhabitationCandidate]))
- alts_to_check src_ty core_ty dcs = case splitTyConApp_maybe core_ty of
- Just (tc, _)
- | isTyConTriviallyInhabited tc
- -> case dcs of
- [] -> return (Left src_ty)
- (_:_) -> do inner <- mkPmId core_ty
- (outer, new_tm_cts) <- build_newtypes inner dcs
- return $ Right (tc, outer, [InhabitationCandidate
- { ic_cs = listToBag new_tm_cts
- , ic_strict_arg_tys = [] }])
-
- | pmIsClosedType core_ty && not (isAbstractTyCon tc)
- -- Don't consider abstract tycons since we don't know what their
- -- constructors are, which makes the results of coverage checking
- -- them extremely misleading.
- -> do
- inner <- mkPmId core_ty -- it would be wrong to unify inner
- alts <- mapM (mkInhabitationCandidate inner) (tyConDataCons tc)
- (outer, new_cts) <- build_newtypes inner dcs
- let wrap_dcs alt = alt{ ic_cs = listToBag new_cts `unionBags` ic_cs alt}
- return $ Right (tc, outer, map wrap_dcs alts)
- -- For other types conservatively assume that they are inhabited.
- _other -> return (Left src_ty)
-
--- | All these types are trivially inhabited
-triviallyInhabitedTyCons :: UniqSet TyCon
-triviallyInhabitedTyCons = mkUniqSet [
- charTyCon, doubleTyCon, floatTyCon, intTyCon, wordTyCon, word8TyCon
- ]
-
-isTyConTriviallyInhabited :: TyCon -> Bool
-isTyConTriviallyInhabited tc = elementOfUniqSet tc triviallyInhabitedTyCons
-
-----------------------------
--- * Detecting vacuous types
-
-{- Note [Checking EmptyCase Expressions]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Empty case expressions are strict on the scrutinee. That is, `case x of {}`
-will force argument `x`. Hence, `covCheckMatches` is not sufficient for checking
-empty cases, because it assumes that the match is not strict (which is true
-for all other cases, apart from EmptyCase). This gave rise to #10746. Instead,
-we do the following:
-
-1. We normalise the outermost type family redex, data family redex or newtype,
- using pmTopNormaliseType (in "GHC.Core.FamInstEnv"). This computes 3
- things:
- (a) A normalised type src_ty, which is equal to the type of the scrutinee in
- source Haskell (does not normalise newtypes or data families)
- (b) The actual normalised type core_ty, which coincides with the result
- topNormaliseType_maybe. This type is not necessarily equal to the input
- type in source Haskell. And this is precicely the reason we compute (a)
- and (c): the reasoning happens with the underlying types, but both the
- patterns and types we print should respect newtypes and also show the
- family type constructors and not the representation constructors.
-
- (c) A list of all newtype data constructors dcs, each one corresponding to a
- newtype rewrite performed in (b).
-
- For an example see also Note [Type normalisation]
- in "GHC.Core.FamInstEnv".
-
-2. Function Check.checkEmptyCase' performs the check:
- - If core_ty is not an algebraic type, then we cannot check for
- inhabitation, so we emit (_ :: src_ty) as missing, conservatively assuming
- that the type is inhabited.
- - If core_ty is an algebraic type, then we unfold the scrutinee to all
- possible constructor patterns, using inhabitationCandidates, and then
- check each one for constraint satisfiability, same as we do for normal
- pattern match checking.
--}
-
--- | A 'SatisfiabilityCheck' based on "NonVoid ty" constraints, e.g. Will
--- check if the @strict_arg_tys@ are actually all inhabited.
--- Returns the old 'Nabla' if all the types are non-void according to 'Nabla'.
-tysAreNonVoid :: RecTcChecker -> [Type] -> SatisfiabilityCheck
-tysAreNonVoid rec_env strict_arg_tys = SC $ \nabla -> do
- all_non_void <- checkAllNonVoid rec_env nabla strict_arg_tys
- -- Check if each strict argument type is inhabitable
- pure $ if all_non_void
- then Just nabla
- else Nothing
-
--- | Implements two performance optimizations, as described in
--- @Note [Strict argument type constraints]@.
-checkAllNonVoid :: RecTcChecker -> Nabla -> [Type] -> DsM Bool
-checkAllNonVoid rec_ts amb_cs strict_arg_tys = do
- let definitely_inhabited = definitelyInhabitedType (nabla_ty_st amb_cs)
- tys_to_check <- filterOutM definitely_inhabited strict_arg_tys
- -- See Note [Fuel for the inhabitation test]
- let rec_max_bound | tys_to_check `lengthExceeds` 1
- = 1
- | otherwise
- = 3
- rec_ts' = setRecTcMaxBound rec_max_bound rec_ts
- allM (nonVoid rec_ts' amb_cs) tys_to_check
-
--- | Checks if a strict argument type of a conlike is inhabitable by a
--- terminating value (i.e, an 'InhabitationCandidate').
--- See @Note [Strict argument type constraints]@.
-nonVoid
- :: RecTcChecker -- ^ The per-'TyCon' recursion depth limit.
- -> Nabla -- ^ The ambient term/type constraints (known to be
- -- satisfiable).
- -> Type -- ^ The strict argument type.
- -> DsM Bool -- ^ 'True' if the strict argument type might be inhabited by
- -- a terminating value (i.e., an 'InhabitationCandidate').
- -- 'False' if it is definitely uninhabitable by anything
- -- (except bottom).
-nonVoid rec_ts amb_cs strict_arg_ty = do
- mb_cands <- inhabitationCandidates amb_cs strict_arg_ty
- case mb_cands of
- Right (tc, _, cands)
- -- See Note [Fuel for the inhabitation test]
- | Just rec_ts' <- checkRecTc rec_ts tc
- -> anyM (cand_is_inhabitable rec_ts' amb_cs) cands
- -- A strict argument type is inhabitable by a terminating value if
- -- at least one InhabitationCandidate is inhabitable.
- _ -> pure True
- -- Either the type is trivially inhabited or we have exceeded the
- -- recursion depth for some TyCon (so bail out and conservatively
- -- claim the type is inhabited).
- where
- -- Checks if an InhabitationCandidate for a strict argument type:
- --
- -- (1) Has satisfiable term and type constraints.
- -- (2) Has 'nonVoid' strict argument types (we bail out of this
- -- check if recursion is detected).
- --
- -- See Note [Strict argument type constraints]
- cand_is_inhabitable :: RecTcChecker -> Nabla
- -> InhabitationCandidate -> DsM Bool
- cand_is_inhabitable rec_ts amb_cs
- (InhabitationCandidate{ ic_cs = new_cs
- , ic_strict_arg_tys = new_strict_arg_tys }) = do
- let (new_ty_cs, new_tm_cs) = partitionTyTmCts new_cs
- fmap isJust $ runSatisfiabilityCheck amb_cs $ mconcat
- [ tyIsSatisfiable False (listToBag new_ty_cs)
- , tmIsSatisfiable (listToBag new_tm_cs)
- , tysAreNonVoid rec_ts new_strict_arg_tys
- ]
-
--- | @'definitelyInhabitedType' ty@ returns 'True' if @ty@ has at least one
--- constructor @C@ such that:
---
--- 1. @C@ has no equality constraints.
--- 2. @C@ has no strict argument types.
---
--- See the @Note [Strict argument type constraints]@.
-definitelyInhabitedType :: TyState -> Type -> DsM Bool
-definitelyInhabitedType ty_st ty = do
- res <- pmTopNormaliseType ty_st ty
- pure $ case res of
- HadRedexes _ cons _ -> any meets_criteria cons
- _ -> False
- where
- meets_criteria :: (Type, DataCon, Type) -> Bool
- meets_criteria (_, con, _) =
- null (dataConEqSpec con) && -- (1)
- null (dataConImplBangs con) -- (2)
-
-{- Note [Strict argument type constraints]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In the ConVar case of clause processing, each conlike K traditionally
-generates two different forms of constraints:
-
-* A term constraint (e.g., x ~ K y1 ... yn)
-* Type constraints from the conlike's context (e.g., if K has type
- forall bs. Q => s1 .. sn -> T tys, then Q would be its type constraints)
-
-As it turns out, these alone are not enough to detect a certain class of
-unreachable code. Consider the following example (adapted from #15305):
-
- data K = K1 | K2 !Void
-
- f :: K -> ()
- f K1 = ()
-
-Even though `f` doesn't match on `K2`, `f` is exhaustive in its patterns. Why?
-Because it's impossible to construct a terminating value of type `K` using the
-`K2` constructor, and thus it's impossible for `f` to ever successfully match
-on `K2`.
-
-The reason is because `K2`'s field of type `Void` is //strict//. Because there
-are no terminating values of type `Void`, any attempt to construct something
-using `K2` will immediately loop infinitely or throw an exception due to the
-strictness annotation. (If the field were not strict, then `f` could match on,
-say, `K2 undefined` or `K2 (let x = x in x)`.)
-
-Since neither the term nor type constraints mentioned above take strict
-argument types into account, we make use of the `nonVoid` function to
-determine whether a strict type is inhabitable by a terminating value or not.
-We call this the "inhabitation test".
-
-`nonVoid ty` returns True when either:
-1. `ty` has at least one InhabitationCandidate for which both its term and type
- constraints are satisfiable, and `nonVoid` returns `True` for all of the
- strict argument types in that InhabitationCandidate.
-2. We're unsure if it's inhabited by a terminating value.
-
-`nonVoid ty` returns False when `ty` is definitely uninhabited by anything
-(except bottom). Some examples:
-
-* `nonVoid Void` returns False, since Void has no InhabitationCandidates.
- (This is what lets us discard the `K2` constructor in the earlier example.)
-* `nonVoid (Int :~: Int)` returns True, since it has an InhabitationCandidate
- (through the Refl constructor), and its term constraint (x ~ Refl) and
- type constraint (Int ~ Int) are satisfiable.
-* `nonVoid (Int :~: Bool)` returns False. Although it has an
- InhabitationCandidate (by way of Refl), its type constraint (Int ~ Bool) is
- not satisfiable.
-* Given the following definition of `MyVoid`:
-
- data MyVoid = MkMyVoid !Void
-
- `nonVoid MyVoid` returns False. The InhabitationCandidate for the MkMyVoid
- constructor contains Void as a strict argument type, and since `nonVoid Void`
- returns False, that InhabitationCandidate is discarded, leaving no others.
-* Whether or not a type is inhabited is undecidable in general.
- See Note [Fuel for the inhabitation test].
-* For some types, inhabitation is evident immediately and we don't need to
- perform expensive tests. See Note [Types that are definitely inhabitable].
-
-Note [Fuel for the inhabitation test]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Whether or not a type is inhabited is undecidable in general. As a result, we
-can run into infinite loops in `nonVoid`. Therefore, we adopt a fuel-based
-approach to prevent that.
-
-Consider the following example:
-
- data Abyss = MkAbyss !Abyss
- stareIntoTheAbyss :: Abyss -> a
- stareIntoTheAbyss x = case x of {}
-
-In principle, stareIntoTheAbyss is exhaustive, since there is no way to
-construct a terminating value using MkAbyss. However, both the term and type
-constraints for MkAbyss are satisfiable, so the only way one could determine
-that MkAbyss is unreachable is to check if `nonVoid Abyss` returns False.
-There is only one InhabitationCandidate for Abyss—MkAbyss—and both its term
-and type constraints are satisfiable, so we'd need to check if `nonVoid Abyss`
-returns False... and now we've entered an infinite loop!
-
-To avoid this sort of conundrum, `nonVoid` uses a simple test to detect the
-presence of recursive types (through `checkRecTc`), and if recursion is
-detected, we bail out and conservatively assume that the type is inhabited by
-some terminating value. This avoids infinite loops at the expense of making
-the coverage checker incomplete with respect to functions like
-stareIntoTheAbyss above. Then again, the same problem occurs with recursive
-newtypes, like in the following code:
-
- newtype Chasm = MkChasm Chasm
- gazeIntoTheChasm :: Chasm -> a
- gazeIntoTheChasm x = case x of {} -- Erroneously warned as non-exhaustive
-
-So this limitation is somewhat understandable.
-
-Note that even with this recursion detection, there is still a possibility that
-`nonVoid` can run in exponential time. Consider the following data type:
-
- data T = MkT !T !T !T
-
-If we call `nonVoid` on each of its fields, that will require us to once again
-check if `MkT` is inhabitable in each of those three fields, which in turn will
-require us to check if `MkT` is inhabitable again... As you can see, the
-branching factor adds up quickly, and if the recursion depth limit is, say,
-100, then `nonVoid T` will effectively take forever.
-
-To mitigate this, we check the branching factor every time we are about to call
-`nonVoid` on a list of strict argument types. If the branching factor exceeds 1
-(i.e., if there is potential for exponential runtime), then we limit the
-maximum recursion depth to 1 to mitigate the problem. If the branching factor
-is exactly 1 (i.e., we have a linear chain instead of a tree), then it's okay
-to stick with a larger maximum recursion depth.
-
-In #17977 we saw that the defaultRecTcMaxBound (100 at the time of writing) was
-too large and had detrimental effect on performance of the coverage checker.
-Given that we only commit to a best effort anyway, we decided to substantially
-decrement the recursion depth to 3, at the cost of precision in some edge cases
-like
-
- data Nat = Z | S Nat
- data Down :: Nat -> Type where
- Down :: !(Down n) -> Down (S n)
- f :: Down (S (S (S (S (S Z))))) -> ()
- f x = case x of {}
-
-Since the coverage won't bother to instantiate Down 4 levels deep to see that it
-is in fact uninhabited, it will emit a inexhaustivity warning for the case.
-
-Note [Types that are definitely inhabitable]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Another microoptimization applies to data types like this one:
-
- data S a = S ![a] !T
-
-Even though there is a strict field of type [a], it's quite silly to call
-nonVoid on it, since it's "obvious" that it is inhabitable. To make this
-intuition formal, we say that a type is definitely inhabitable (DI) if:
-
- * It has at least one constructor C such that:
- 1. C has no equality constraints (since they might be unsatisfiable)
- 2. C has no strict argument types (since they might be uninhabitable)
-
-It's relatively cheap to check if a type is DI, so before we call `nonVoid`
-on a list of strict argument types, we filter out all of the DI ones.
--}
-
---------------------------------------------
--- * Providing positive evidence for a Nabla
-
--- | @provideEvidence vs n nabla@ returns a list of
--- at most @n@ (but perhaps empty) refinements of @nabla@ that instantiate
--- @vs@ to compatible constructor applications or wildcards.
--- Negative information is only retained if literals are involved or when
--- for recursive GADTs.
-provideEvidence :: [Id] -> Int -> Nabla -> DsM [Nabla]
-provideEvidence = go
- where
- go _ 0 _ = pure []
- go [] _ nabla = pure [nabla]
- go (x:xs) n nabla = do
- tracePm "provideEvidence" (ppr x $$ ppr xs $$ ppr nabla $$ ppr n)
- let VI _ pos neg _ _ = lookupVarInfo (nabla_tm_st nabla) x
- case pos of
- _:_ -> do
- -- All solutions must be valid at once. Try to find candidates for their
- -- fields. Example:
- -- f x@(Just _) True = case x of SomePatSyn _ -> ()
- -- after this clause, we want to report that
- -- * @f Nothing _@ is uncovered
- -- * @f x False@ is uncovered
- -- where @x@ will have two possibly compatible solutions, @Just y@ for
- -- some @y@ and @SomePatSyn z@ for some @z@. We must find evidence for @y@
- -- and @z@ that is valid at the same time. These constitute arg_vas below.
- let arg_vas = concatMap (\(_cl, _tvs, args) -> args) pos
- go (arg_vas ++ xs) n nabla
- []
- -- When there are literals involved, just print negative info
- -- instead of listing missed constructors
- | notNull [ l | PmAltLit l <- pmAltConSetElems neg ]
- -> go xs n nabla
- [] -> try_instantiate x xs n nabla
-
- -- | Tries to instantiate a variable by possibly following the chain of
- -- newtypes and then instantiating to all ConLikes of the wrapped type's
- -- minimal residual COMPLETE set.
- try_instantiate :: Id -> [Id] -> Int -> Nabla -> DsM [Nabla]
- -- Convention: x binds the outer constructor in the chain, y the inner one.
- try_instantiate x xs n nabla = do
- (_src_ty, dcs, rep_ty) <- tntrGuts <$> pmTopNormaliseType (nabla_ty_st nabla) (idType x)
- let build_newtype (x, nabla) (_ty, dc, arg_ty) = do
- y <- lift $ mkPmId arg_ty
- -- Newtypes don't have existentials (yet?!), so passing an empty
- -- list as ex_tvs.
- nabla' <- addConCt nabla x (PmAltConLike (RealDataCon dc)) [] [y]
- pure (y, nabla')
- runMaybeT (foldlM build_newtype (x, nabla) dcs) >>= \case
- Nothing -> pure []
- Just (y, newty_nabla) -> do
- -- Pick a COMPLETE set and instantiate it (n at max). Take care of ⊥.
- let vi = lookupVarInfo (nabla_tm_st newty_nabla) y
- rcm <- case splitTyConApp_maybe rep_ty of
- Nothing -> pure (vi_rcm vi)
- Just (tc, _) -> addTyConMatches tc (vi_rcm vi)
- mb_cls <- pickMinimalCompleteSet rep_ty rcm
- case uniqDSetToList <$> mb_cls of
- Just cls -> do
- nablas <- instantiate_cons y rep_ty xs n newty_nabla cls
- if null nablas && vi_bot vi /= IsNotBot
- then go xs n newty_nabla -- bot is still possible. Display a wildcard!
- else pure nablas
- Nothing -> go xs n newty_nabla -- no COMPLETE sets ==> inhabited
-
- instantiate_cons :: Id -> Type -> [Id] -> Int -> Nabla -> [ConLike] -> DsM [Nabla]
- instantiate_cons _ _ _ _ _ [] = pure []
- instantiate_cons _ _ _ 0 _ _ = pure []
- instantiate_cons _ ty xs n nabla _
- -- We don't want to expose users to GHC-specific constructors for Int etc.
- | fmap (isTyConTriviallyInhabited . fst) (splitTyConApp_maybe ty) == Just True
- = go xs n nabla
- instantiate_cons x ty xs n nabla (cl:cls) = do
- env <- dsGetFamInstEnvs
- case guessConLikeUnivTyArgsFromResTy env ty cl of
- Nothing -> pure [nabla] -- No idea how to refine this one, so just finish off with a wildcard
- Just arg_tys -> do
- (tvs, arg_vars, new_ty_cs, strict_arg_tys) <- mkOneConFull arg_tys cl
- let new_tm_cs = unitBag (TmConCt x (PmAltConLike cl) tvs arg_vars)
- -- Now check satifiability
- mb_nabla <- pmIsSatisfiable nabla new_ty_cs new_tm_cs strict_arg_tys
- tracePm "instantiate_cons" (vcat [ ppr x
- , ppr (idType x)
- , ppr ty
- , ppr cl
- , ppr arg_tys
- , ppr new_tm_cs
- , ppr new_ty_cs
- , ppr strict_arg_tys
- , ppr nabla
- , ppr mb_nabla
- , ppr n ])
- con_nablas <- case mb_nabla of
- Nothing -> pure []
- -- NB: We don't prepend arg_vars as we don't have any evidence on
- -- them and we only want to split once on a data type. They are
- -- inhabited, otherwise pmIsSatisfiable would have refuted.
- Just nabla' -> go xs n nabla'
- other_cons_nablas <- instantiate_cons x ty xs (n - length con_nablas) nabla cls
- pure (con_nablas ++ other_cons_nablas)
-
-pickMinimalCompleteSet :: Type -> ResidualCompleteMatches -> DsM (Maybe ConLikeSet)
-pickMinimalCompleteSet ty rcm = do
- env <- dsGetFamInstEnvs
- pure $ case filter (all (is_valid env) . uniqDSetToList) (getRcm rcm) of
- [] -> Nothing
- clss' -> Just (minimumBy (comparing sizeUniqDSet) clss')
- where
- is_valid :: FamInstEnvs -> ConLike -> Bool
- is_valid env cl = isJust (guessConLikeUnivTyArgsFromResTy env ty cl)
-
--- | Finds a representant of the semantic equality class of the given @e@.
--- Which is the @x@ of a @let x = e'@ constraint (with @e@ semantically
--- equivalent to @e'@) we encountered earlier, or a fresh identifier if
--- there weren't any such constraints.
-representCoreExpr :: Nabla -> CoreExpr -> DsM (Nabla, Id)
-representCoreExpr nabla@MkNabla{ nabla_tm_st = ts@TmSt{ ts_reps = reps } } e
- | Just rep <- lookupCoreMap reps e = pure (nabla, rep)
- | otherwise = do
- rep <- mkPmId (exprType e)
- let reps' = extendCoreMap reps e rep
- let nabla' = nabla{ nabla_tm_st = ts{ ts_reps = reps' } }
- pure (nabla', rep)
-
-- | Inspects a 'PmCoreCt' @let x = e@ by recording constraints for @x@ based
-- on the shape of the 'CoreExpr' @e@. Examples:
--
@@ -1795,7 +974,7 @@ addCoreCt nabla x e = do
when (not (isNewDataCon dc)) $
modifyT $ \nabla -> addNotBotCt nabla x
-- 2. @a_1 ~ tau_1, ..., a_n ~ tau_n@ for fresh @a_i@. See also #17703
- modifyT $ \nabla -> MaybeT $ addPmCts nabla (listToBag ty_cts)
+ modifyT $ \nabla -> MaybeT $ addPhiCts nabla (listToBag ty_cts)
-- 3. @y_1 ~ e_1, ..., y_m ~ e_m@ for fresh @y_i@
arg_ids <- traverse bind_expr vis_args
-- 4. @x ~ K as ys@
@@ -1813,12 +992,119 @@ addCoreCt nabla x e = do
pm_alt_con_app :: Id -> PmAltCon -> [TyVar] -> [Id] -> StateT Nabla (MaybeT DsM) ()
pm_alt_con_app x con tvs args = modifyT $ \nabla -> addConCt nabla x con tvs args
+-- | Finds a representant of the semantic equality class of the given @e@.
+-- Which is the @x@ of a @let x = e'@ constraint (with @e@ semantically
+-- equivalent to @e'@) we encountered earlier, or a fresh identifier if
+-- there weren't any such constraints.
+representCoreExpr :: Nabla -> CoreExpr -> DsM (Nabla, Id)
+representCoreExpr nabla@MkNabla{ nabla_tm_st = ts@TmSt{ ts_reps = reps } } e
+ | Just rep <- lookupCoreMap reps e = pure (nabla, rep)
+ | otherwise = do
+ rep <- mkPmId (exprType e)
+ let reps' = extendCoreMap reps e rep
+ let nabla' = nabla{ nabla_tm_st = ts{ ts_reps = reps' } }
+ pure (nabla', rep)
+
-- | Like 'modify', but with an effectful modifier action
modifyT :: Monad m => (s -> m s) -> StateT s m ()
modifyT f = StateT $ fmap ((,) ()) . f
-{- Note [Detecting pattern synonym applications in expressions]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+{- Note [The Pos/Neg invariant]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Invariant applying to each VarInfo: Whenever we have @C @tvs args@ in 'vi_pos',
+any entry in 'vi_neg' must be incomparable to C (return Nothing) according to
+'eqPmAltCons'. Those entries that are comparable either lead to a refutation
+or are redundant. Examples:
+* @x ~ Just y@, @x ≁ [Just]@. 'eqPmAltCon' returns @Equal@, so refute.
+* @x ~ Nothing@, @x ≁ [Just]@. 'eqPmAltCon' returns @Disjoint@, so negative
+ info is redundant and should be discarded.
+* @x ~ I# y@, @x ≁ [4,2]@. 'eqPmAltCon' returns @PossiblyOverlap@, so orthogal.
+ We keep this info in order to be able to refute a redundant match on i.e. 4
+ later on.
+
+This carries over to pattern synonyms and overloaded literals. Say, we have
+ pattern Just42 = Just 42
+ case Just42 of x
+ Nothing -> ()
+ Just _ -> ()
+Even though we had a solution for the value abstraction called x here in form
+of a PatSynCon Just42, this solution is incomparable to both Nothing and
+Just. Hence we retain the info in vi_neg, which eventually allows us to detect
+the complete pattern match.
+
+The Pos/Neg invariant extends to vi_rcm, which essentially stores positive
+information. We make sure that vi_neg and vi_rcm never overlap. This isn't
+strictly necessary since vi_rcm is just a cache, so doesn't need to be
+accurate: Every suggestion of a possible ConLike from vi_rcm might be
+refutable by the type oracle anyway. But it helps to maintain sanity while
+debugging traces.
+
+Note [Why record both positive and negative info?]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You might think that knowing positive info (like x ~ Just y) would render
+negative info irrelevant, but not so because of pattern synonyms. E.g we might
+know that x cannot match (Foo 4), where pattern Foo p = Just p
+
+Also overloaded literals themselves behave like pattern synonyms. E.g if
+postively we know that (x ~ I# y), we might also negatively want to record that
+x does not match 45 f 45 = e2 f (I# 22#) = e3 f 45 = e4 --
+Overlapped
+
+Note [TmState invariants]
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The term oracle state is never obviously (i.e., without consulting the type
+oracle or doing inhabitation testing) contradictory. This implies a few
+invariants:
+* Whenever vi_pos overlaps with vi_neg according to 'eqPmAltCon', we refute.
+ This is implied by the Note [Pos/Neg invariant].
+* Whenever vi_neg subsumes a COMPLETE set, we refute. We consult vi_rcm to
+ detect this, but we could just compare whole COMPLETE sets to vi_neg every
+ time, if it weren't for performance.
+
+Maintaining these invariants in 'addVarCt' (the core of the term oracle) and
+'addNotConCt' is subtle.
+* Merging VarInfos. Example: Add the fact @x ~ y@ (see 'equate').
+ - (COMPLETE) If we had @x ≁ True@ and @y ≁ False@, then we get
+ @x ≁ [True,False]@. This is vacuous by matter of comparing to the built-in
+ COMPLETE set, so should refute.
+ - (Pos/Neg) If we had @x ≁ True@ and @y ~ True@, we have to refute.
+* Adding positive information. Example: Add the fact @x ~ K ys@ (see 'addConCt')
+ - (Neg) If we had @x ≁ K@, refute.
+ - (Pos) If we had @x ~ K2@, and that contradicts the new solution according to
+ 'eqPmAltCon' (ex. K2 is [] and K is (:)), then refute.
+ - (Refine) If we had @x ≁ K zs@, unify each y with each z in turn.
+* Adding negative information. Example: Add the fact @x ≁ Nothing@ (see 'addNotConCt')
+ - (Refut) If we have @x ~ K ys@, refute.
+ - (COMPLETE) If K=Nothing and we had @x ≁ Just@, then we get
+ @x ≁ [Just,Nothing]@. This is vacuous by matter of comparing to the built-in
+ COMPLETE set, so should refute.
+
+Note that merging VarInfo in equate can be done by calling out to 'addConCt' and
+'addNotConCt' for each of the facts individually.
+
+Note [Representation of Strings in TmState]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Instead of treating regular String literals as a PmLits, we treat it as a list
+of characters in the oracle for better overlap reasoning. The following example
+shows why:
+
+ f :: String -> ()
+ f ('f':_) = ()
+ f "foo" = ()
+ f _ = ()
+
+The second case is redundant, and we like to warn about it. Therefore either
+the oracle will have to do some smart conversion between the list and literal
+representation or treat is as the list it really is at runtime.
+
+The "smart conversion" has the advantage of leveraging the more compact literal
+representation wherever possible, but is really nasty to get right with negative
+equalities: Just think of how to encode @x /= "foo"@.
+The "list" option is far simpler, but incurs some overhead in representation and
+warning messages (which can be alleviated by someone with enough dedication).
+
+Note [Detecting pattern synonym applications in expressions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the moment we fail to detect pattern synonyms in scrutinees and RHS of
guards. This could be alleviated with considerable effort and complexity, but
the returns are meager. Consider:
@@ -1838,4 +1124,644 @@ knows "If it's a P, then its field is 15".
This is a pretty narrow use case and I don't think we should to try to fix it
until a user complains energetically.
+
+Note [Completeness checking with required Thetas]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider the situation in #11224
+
+ import Text.Read (readMaybe)
+ pattern PRead :: Read a => () => a -> String
+ pattern PRead x <- (readMaybe -> Just x)
+ f :: String -> Int
+ f (PRead x) = x
+ f (PRead xs) = length xs
+ f _ = 0
+
+Is the first match exhaustive on the PRead synonym? Should the second line thus
+deemed redundant? The answer is, of course, No! The required theta is like a
+hidden parameter which must be supplied at the pattern match site, so PRead
+is much more like a view pattern (where behavior depends on the particular value
+passed in).
+The simple solution here is to forget in 'addNotConCt' that we matched
+on synonyms with a required Theta like @PRead@, so that subsequent matches on
+the same constructor are never flagged as redundant. The consequence is that
+we no longer detect the actually redundant match in
+
+ g :: String -> Int
+ g (PRead x) = x
+ g (PRead y) = y -- redundant!
+ g _ = 0
+
+But that's a small price to pay, compared to the proper solution here involving
+storing required arguments along with the PmAltConLike in 'vi_neg'.
+
+Note [Strict fields and variables of unlifted type]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Binders of unlifted type (and strict fields) are unlifted by construction;
+they are conceived with an implicit @≁⊥@ constraint to begin with. Hence,
+desugaring in "GHC.HsToCore.PmCheck" is entirely unconcerned by strict fields,
+since the forcing happens *before* pattern matching.
+And the φ constructor constraints emitted by 'GHC.HsToCore.PmCheck.checkGrd'
+have complex binding semantics (binding type constraints and unlifted fields),
+so unliftedness semantics are entirely confined to the oracle.
+
+These are the moving parts:
+
+ 1. For each strict (or more generally, unlifted) field @s@ of a 'PhiConCt'
+ we have to add a @s ≁ ⊥@ constraint in the corresponding case of
+ 'addPhiTmCt'. Strict fields are devoid of ⊥ by construction, there's
+ nothing that a bang pattern would act on. Example from #18341:
+
+ data T = MkT !Int
+ f :: T -> ()
+ f (MkT _) | False = () -- inaccessible
+ f (MkT !_) | False = () -- redundant, not only inaccessible!
+ f _ = ()
+
+ The second clause desugars to @MkT n <- x, !n@. When coverage checked,
+ the 'PmCon' @MkT n <- x@ refines the set of values that reach the bang
+ pattern with the φ constraints @MkT n <- x@ (Nothing surprising so far).
+ Upon that constraint, it disperses into two lower-level δ constraints
+ @x ~ MkT n, n ≁ ⊥@ per Equation (3) in Figure 7 of the paper.
+
+ Checking the 'PmBang' @!n@ will then try to add the
+ constraint @n ~ ⊥@ to this set to get the diverging set, which is found
+ to be empty. Hence the whole clause is detected as redundant, as
+ expected.
+
+ 2. Similarly, when performing the 'inhabitationTest', when instantiating a
+ constructor we call 'instCon', which generates a higher-level φ
+ constructor constraint.
+
+ 3. The preceding points handle unlifted constructor fields, but there also
+ are regular binders of unlifted type.
+ Since the oracle as implemented has no notion of scoping and bindings,
+ we can't know *when* an unlifted variable comes into scope. But that's
+ not actually a problem, because we can just add the @x ≁ ⊥@ to the
+ 'emptyVarInfo' when we first encounter it.
+-}
+
+-------------------------
+-- * Inhabitation testing
+--
+-- Figure 8 in the LYG paper.
+
+tyStateRefined :: TyState -> TyState -> Bool
+-- Makes use of the fact that the two TyStates we compare will never have the
+-- same sequence number. It is invalid to call this function when a is not a
+-- refinement of b or vice versa!
+tyStateRefined a b = ty_st_n a /= ty_st_n b
+
+markDirty :: Id -> Nabla -> Nabla
+markDirty x nabla@MkNabla{nabla_tm_st = ts@TmSt{ts_dirty = dirty} } =
+ nabla{ nabla_tm_st = ts{ ts_dirty = extendDVarSet dirty x } }
+
+traverseDirty :: Monad m => (VarInfo -> m VarInfo) -> TmState -> m TmState
+traverseDirty f ts@TmSt{ts_facts = env, ts_dirty = dirty} =
+ go (uniqDSetToList dirty) env
+ where
+ go [] env = pure ts{ts_facts=env}
+ go (x:xs) !env = do
+ vi' <- f (lookupVarInfo ts x)
+ go xs (setEntrySDIE env x vi')
+
+traverseAll :: Monad m => (VarInfo -> m VarInfo) -> TmState -> m TmState
+traverseAll f ts@TmSt{ts_facts = env} = do
+ env' <- traverseSDIE f env
+ pure ts{ts_facts = env'}
+
+-- | Makes sure the given 'Nabla' is still inhabited, by trying to instantiate
+-- all dirty variables (or all variables when the 'TyState' changed) to concrete
+-- inhabitants. It returns a 'Nabla' with the *same* inhabitants, but with some
+-- amount of work cached (like failed instantiation attempts) from the test.
+--
+-- The \(∇ ⊢ x inh\) judgment form in Figure 8 of the LYG paper.
+inhabitationTest :: Int -> TyState -> Nabla -> MaybeT DsM Nabla
+inhabitationTest 0 _ nabla = pure nabla
+inhabitationTest fuel old_ty_st nabla@MkNabla{ nabla_tm_st = ts } = do
+ -- lift $ tracePm "inhabitation test" $ vcat
+ -- [ ppr fuel
+ -- , ppr old_ty_st
+ -- , ppr nabla
+ -- , text "tyStateRefined:" <+> ppr (tyStateRefined old_ty_st (nabla_ty_st nabla))
+ -- ]
+ -- When type state didn't change, we only need to traverse dirty VarInfos
+ ts' <- if tyStateRefined old_ty_st (nabla_ty_st nabla)
+ then traverseAll test_one ts
+ else traverseDirty test_one ts
+ pure nabla{ nabla_tm_st = ts'{ts_dirty=emptyDVarSet}}
+ where
+ nabla_not_dirty = nabla{ nabla_tm_st = ts{ts_dirty=emptyDVarSet} }
+ test_one :: VarInfo -> MaybeT DsM VarInfo
+ test_one vi = do
+ lift (varNeedsTesting old_ty_st nabla vi) >>= \case
+ True -> do
+ -- tracPm "test_one" (ppr vi)
+ -- No solution yet and needs testing
+ -- We have to test with a Nabla where all dirty bits are cleared
+ instantiate (fuel-1) nabla_not_dirty vi
+ _ -> pure vi
+
+-- | Checks whether the given 'VarInfo' needs to be tested for inhabitants.
+--
+-- 1. If it already has a solution, we don't have to test.
+-- 2. If it's marked dirty because of new negative term constraints, we have
+-- to test.
+-- 3. Otherwise, if the type state didn't change, we don't need to test.
+-- 4. If the type state changed, we compare normalised source types. No need
+-- to test if unchanged.
+varNeedsTesting :: TyState -> Nabla -> VarInfo -> DsM Bool
+varNeedsTesting _ _ vi
+ | notNull (vi_pos vi) = pure False
+varNeedsTesting _ MkNabla{nabla_tm_st=tm_st} vi
+ | elemDVarSet (vi_id vi) (ts_dirty tm_st) = pure True
+varNeedsTesting old_ty_st MkNabla{nabla_ty_st=new_ty_st} _
+ -- Same type state => still inhabited
+ | not (tyStateRefined old_ty_st new_ty_st) = pure False
+varNeedsTesting old_ty_st MkNabla{nabla_ty_st=new_ty_st} vi = do
+ -- These normalisations are relatively expensive, but still better than having
+ -- to perform a full inhabitation test
+ (_, _, old_norm_ty) <- tntrGuts <$> pmTopNormaliseType old_ty_st (idType $ vi_id vi)
+ (_, _, new_norm_ty) <- tntrGuts <$> pmTopNormaliseType new_ty_st (idType $ vi_id vi)
+ if old_norm_ty `eqType` new_norm_ty
+ then pure False
+ else pure True
+
+-- | Returns (Just vi) if at least one member of each ConLike in the COMPLETE
+-- set satisfies the oracle
+--
+-- Internally uses and updates the ConLikeSets in vi_rcm.
+--
+-- NB: Does /not/ filter each ConLikeSet with the oracle; members may
+-- remain that do not statisfy it. This lazy approach just
+-- avoids doing unnecessary work.
+instantiate :: Int -> Nabla -> VarInfo -> MaybeT DsM VarInfo
+instantiate fuel nabla vi = instBot fuel nabla vi <|> instCompleteSets fuel nabla vi
+
+-- | The \(⊢_{Bot}\) rule from the paper
+instBot :: Int -> Nabla -> VarInfo -> MaybeT DsM VarInfo
+instBot _fuel nabla vi = do
+ _nabla' <- addBotCt nabla (vi_id vi)
+ pure vi
+
+addNormalisedTypeMatches :: Nabla -> Id -> DsM (ResidualCompleteMatches, Nabla)
+addNormalisedTypeMatches nabla@MkNabla{ nabla_ty_st = ty_st } x
+ = trvVarInfo add_matches nabla x
+ where
+ add_matches vi@VI{ vi_rcm = rcm }
+ -- important common case, shaving down allocations of PmSeriesG by -5%
+ | isRcmInitialised rcm = pure (rcm, vi)
+ add_matches vi@VI{ vi_rcm = rcm } = do
+ norm_res_ty <- normaliseSourceTypeWHNF ty_st (idType x)
+ env <- dsGetFamInstEnvs
+ rcm' <- case splitReprTyConApp_maybe env norm_res_ty of
+ Just (rep_tc, _args, _co) -> addTyConMatches rep_tc rcm
+ Nothing -> addCompleteMatches rcm
+ pure (rcm', vi{ vi_rcm = rcm' })
+
+-- | Does a 'splitTyConApp_maybe' and then tries to look through a data family
+-- application to find the representation TyCon, to which the data constructors
+-- are attached. Returns the representation TyCon, the TyCon application args
+-- and a representational coercion that will be Refl for non-data family apps.
+splitReprTyConApp_maybe :: FamInstEnvs -> Type -> Maybe (TyCon, [Type], Coercion)
+splitReprTyConApp_maybe env ty =
+ uncurry (tcLookupDataFamInst env) <$> splitTyConApp_maybe ty
+
+-- | This is the |-Inst rule from the paper (section 4.5). Tries to
+-- find an inhabitant in every complete set by instantiating with one their
+-- constructors. If there is any complete set where we can't find an
+-- inhabitant, the whole thing is uninhabited. It returns the updated 'VarInfo'
+-- where all the attempted ConLike instantiations have been purged from the
+-- 'ResidualCompleteMatches', which functions as a cache.
+instCompleteSets :: Int -> Nabla -> VarInfo -> MaybeT DsM VarInfo
+instCompleteSets fuel nabla vi = do
+ let x = vi_id vi
+ (rcm, nabla) <- lift (addNormalisedTypeMatches nabla x)
+ nabla <- foldM (\nabla cls -> instCompleteSet fuel nabla x cls) nabla (getRcm rcm)
+ pure (lookupVarInfo (nabla_tm_st nabla) x)
+
+anyConLikeSolution :: (ConLike -> Bool) -> [PmAltConApp] -> Bool
+anyConLikeSolution p = any (go . paca_con)
+ where
+ go (PmAltConLike cl) = p cl
+ go _ = False
+
+-- | @instCompleteSet fuel nabla nabla cls@ iterates over @cls@ until it finds
+-- the first inhabited ConLike (as per 'instCon'). Any failed instantiation
+-- attempts of a ConLike are recorded as negative information in the returned
+-- 'Nabla', so that later calls to this function can skip repeatedly fruitless
+-- instantiation of that same constructor.
+--
+-- Note that the returned Nabla is just a different representation of the
+-- original Nabla, not a proper refinement! No positive information will be
+-- added, only negative information from failed instantiation attempts,
+-- entirely as an optimisation.
+instCompleteSet :: Int -> Nabla -> Id -> ConLikeSet -> MaybeT DsM Nabla
+instCompleteSet fuel nabla x cs
+ | anyConLikeSolution (`elementOfUniqDSet` cs) (vi_pos vi)
+ -- No need to instantiate a constructor of this COMPLETE set if we already
+ -- have a solution!
+ = pure nabla
+ | otherwise
+ = go nabla (sorted_candidates cs)
+ where
+ vi = lookupVarInfo (nabla_tm_st nabla) x
+
+ sorted_candidates :: ConLikeSet -> [ConLike]
+ sorted_candidates cs
+ -- If there aren't many candidates, we can try to sort them by number of
+ -- strict fields, type constraints, etc., so that we are fast in the
+ -- common case
+ -- (either many simple constructors *or* few "complicated" ones).
+ | sizeUniqDSet cs <= 5 = sortBy compareConLikeTestability (uniqDSetToList cs)
+ | otherwise = uniqDSetToList cs
+
+ go :: Nabla -> [ConLike] -> MaybeT DsM Nabla
+ go _ [] = mzero
+ go nabla (RealDataCon dc:_)
+ -- See Note [DataCons that are definitely inhabitable]
+ -- Recall that dc can't be in vi_neg, because then it would be
+ -- deleted from the residual COMPLETE set.
+ | isDataConTriviallyInhabited dc
+ = pure nabla
+ go nabla (con:cons) = do
+ let x = vi_id vi
+ let recur_not_con = do
+ nabla' <- addNotConCt nabla x (PmAltConLike con)
+ go nabla' cons
+ (nabla <$ instCon fuel nabla x con) -- return the original nabla, not the
+ -- refined one!
+ <|> recur_not_con -- Assume that x can't be con. Encode that fact
+ -- with addNotConCt and recur.
+
+-- | Is this 'DataCon' trivially inhabited, that is, without needing to perform
+-- any inhabitation testing because of strict/unlifted fields or type
+-- equalities? See Note [DataCons that are definitely inhabitable]
+isDataConTriviallyInhabited :: DataCon -> Bool
+isDataConTriviallyInhabited dc
+ | isTyConTriviallyInhabited (dataConTyCon dc) = True
+isDataConTriviallyInhabited dc =
+ null (dataConTheta dc) && -- (1)
+ null (dataConImplBangs dc) && -- (2)
+ null (dataConUnliftedFieldTys dc) -- (3)
+
+dataConUnliftedFieldTys :: DataCon -> [Type]
+dataConUnliftedFieldTys =
+ -- A levity polymorphic field requires an inhabitation test, hence compare to
+ -- @Just True@
+ filter ((== Just True) . isLiftedType_maybe) . map scaledThing . dataConOrigArgTys
+
+isTyConTriviallyInhabited :: TyCon -> Bool
+isTyConTriviallyInhabited tc = elementOfUniqSet tc triviallyInhabitedTyCons
+
+-- | All these types are trivially inhabited
+triviallyInhabitedTyCons :: UniqSet TyCon
+triviallyInhabitedTyCons = mkUniqSet [
+ charTyCon, doubleTyCon, floatTyCon, intTyCon, wordTyCon, word8TyCon
+ ]
+
+compareConLikeTestability :: ConLike -> ConLike -> Ordering
+-- We should instantiate DataCons first, because they are likely to occur in
+-- multiple COMPLETE sets at once and we might find that multiple COMPLETE sets
+-- are inhabitated by instantiating only a single DataCon.
+compareConLikeTestability PatSynCon{} _ = GT
+compareConLikeTestability _ PatSynCon{} = GT
+compareConLikeTestability (RealDataCon a) (RealDataCon b) = mconcat
+ -- Thetas are most expensive to check, as they might incur a whole new round
+ -- of inhabitation testing
+ [ comparing (fast_length . dataConTheta)
+ -- Unlifted or strict fields only incur an inhabitation test for that
+ -- particular field. Still something to avoid.
+ , comparing unlifted_or_strict_fields
+ ] a b
+ where
+ fast_length :: [a] -> Int
+ fast_length xs = atLength length 6 xs 5 -- @min 6 (length xs)@, but O(1)
+
+ -- An upper bound on the number of strict or unlifted fields. Approximate in
+ -- the unlikely bogus case of an unlifted field that has a bang.
+ unlifted_or_strict_fields :: DataCon -> Int
+ unlifted_or_strict_fields dc = fast_length (dataConImplBangs dc)
+ + fast_length (dataConUnliftedFieldTys dc)
+
+-- | @instCon fuel nabla (x::res_ty) K@ tries to instantiate @x@ to @K@.
+-- This is the \(Inst\) function from Figure 8 of the LYG paper.
+--
+-- As a first step, it tries to guess the universal type arguments of @K@ from
+-- the @res_ty@ and @K@'s result type, so that we have
+--
+-- K @arg_tys :: forall es. Q => t1 -> ... -> tn -> res_ty
+--
+-- See 'guessConLikeUnivTyArgsFromResTy' for when it might fail to guess (only
+-- for certain pattern synonyms).
+-- If the function /fails/ to guess, 'instCon' trivially succeeds, because it
+-- can't be sure if @x@ could be instantiated to @K@ and has to be
+-- conservative).
+-- If the function /succeeds/ in guessing @arg_tys@, it does the necessary
+-- substitution and instantiation dance for @K@'s field types, which may still
+-- reference existential type variables of @K@. So it performs the following steps:
+--
+-- * It accumulates a substitution mapping @K@'s universal type variables,
+-- which are substituted for @arg_tys@.
+-- * It instantiates fresh binders for the other type variables @es@ bound by
+-- @K@ and adds it to the substitution, so that we have
+-- @K \@arg_tys \@ex_tvs :: Q => t1' -> ... -> tn' -> res_ty@
+-- * It substitutes the 'ThetaType' @Q@ for type constraints @gammas@ to add
+-- * It substitutes and conjures new binders @arg_ids@ for the argument types
+-- @t1' ... tn'@.
+--
+-- Finally, it adds a 'PhiConCt' constructor constraint
+-- @K ex_tvs gammas arg_ids <- x@ which handles the details regarding type
+-- constraints and unlifted fields.
+instCon :: Int -> Nabla -> Id -> ConLike -> MaybeT DsM Nabla
+instCon fuel nabla@MkNabla{nabla_ty_st = ty_st} x con = MaybeT $ do
+ env <- dsGetFamInstEnvs
+ let res_ty = idType x
+ norm_res_ty <- normaliseSourceTypeWHNF ty_st res_ty
+ let mb_arg_tys = guessConLikeUnivTyArgsFromResTy env norm_res_ty con
+ case mb_arg_tys of
+ Just arg_tys -> do
+ let (univ_tvs, ex_tvs, eq_spec, thetas, _req_theta, field_tys, _con_res_ty)
+ = conLikeFullSig con
+ -- (1) Substitute universals for type arguments
+ let subst_univ = zipTvSubst univ_tvs arg_tys
+ -- (2) Instantiate fresh existentials as arguments to the constructor.
+ -- This is important for instantiating the Thetas and field types.
+ (subst, _) <- cloneTyVarBndrs subst_univ ex_tvs <$> getUniqueSupplyM
+ let field_tys' = substTys subst $ map scaledThing field_tys
+ -- (3) All constraints bound by the constructor (alpha-renamed), these are added
+ -- to the type oracle
+ let gammas = substTheta subst (eqSpecPreds eq_spec ++ thetas)
+ -- (4) Instantiate fresh term variables as arguments to the constructor
+ arg_ids <- mapM mkPmId field_tys'
+ tracePm "instCon" $ vcat
+ [ ppr x <+> dcolon <+> ppr res_ty
+ , text "In WHNF:" <+> ppr (isSourceTypeInWHNF res_ty) <+> ppr norm_res_ty
+ , ppr con <+> dcolon <+> text "... ->" <+> ppr _con_res_ty
+ , ppr (zipWith (\tv ty -> ppr tv <+> char '↦' <+> ppr ty) univ_tvs arg_tys)
+ , ppr gammas
+ , ppr (map (\x -> ppr x <+> dcolon <+> ppr (idType x)) arg_ids)
+ , ppr fuel
+ ]
+ -- Finally add the constraint
+ runMaybeT $ do
+ -- Case (2) of Note [Strict fields and variables of unlifted type]
+ let alt = PmAltConLike con
+ nabla' <- addPhiTmCt nabla (PhiConCt x alt ex_tvs gammas arg_ids)
+ let branching_factor = length $ filterUnliftedFields alt arg_ids
+ -- See Note [Fuel for the inhabitation test]
+ let new_fuel
+ | branching_factor <= 1 = fuel
+ | otherwise = min fuel 2
+ inhabitationTest new_fuel (nabla_ty_st nabla) nabla'
+ Nothing -> pure (Just nabla) -- Could not guess arg_tys. Just assume inhabited
+
+-- | Guess the universal argument types of a ConLike from an instantiation of
+-- its (normalised!) result type. So, given
+--
+-- K :: forall us. forall es. Q => t1 -> ... -> tn -> con_res_ty
+--
+-- It tries to guess @arg_tys@ by matching @norm_res_ty@ and @con_res_ty@, such that
+--
+-- K @arg_tys :: forall es. Q' => t1' -> ... -> tn' -> norm_res_ty
+--
+-- Rather easy for DataCons, but not so much for PatSynCons. See Note [Pattern
+-- synonym result type] in "GHC.Core.PatSyn".
+guessConLikeUnivTyArgsFromResTy :: FamInstEnvs -> Type -> ConLike -> Maybe [Type]
+guessConLikeUnivTyArgsFromResTy env norm_res_ty (RealDataCon dc) = do
+ -- splitReprTyConApp_maybe rather than splitTyConApp_maybe because of data families.
+ (rep_tc, tc_args, _co) <- splitReprTyConApp_maybe env norm_res_ty
+ if rep_tc == dataConTyCon dc
+ then Just tc_args
+ else Nothing
+guessConLikeUnivTyArgsFromResTy _ norm_res_ty (PatSynCon ps) = do
+ -- We are successful if we managed to instantiate *every* univ_tv of con.
+ -- This is difficult and bound to fail in some cases, see
+ -- Note [Pattern synonym result type] in GHC.Core.PatSyn. So we just try our best
+ -- here and be sure to return an instantiation when we can substitute every
+ -- universally quantified type variable.
+ -- We *could* instantiate all the other univ_tvs just to fresh variables, I
+ -- suppose, but that means we get weird field types for which we don't know
+ -- anything. So we prefer to keep it simple here.
+ let (univ_tvs,_,_,_,_,con_res_ty) = patSynSig ps
+ subst <- tcMatchTy con_res_ty norm_res_ty
+ traverse (lookupTyVar subst) univ_tvs
+
+{- Note [Fuel for the inhabitation test]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Whether or not a type is inhabited is undecidable in general. As a result, we
+can run into infinite loops in `inhabitationTest`. Therefore, we adopt a
+fuel-based approach to prevent that.
+
+Consider the following example:
+
+ data Abyss = MkAbyss !Abyss
+ stareIntoTheAbyss :: Abyss -> a
+ stareIntoTheAbyss x = case x of {}
+
+In principle, stareIntoTheAbyss is exhaustive, since there is no way to
+construct a terminating value using MkAbyss. But this can't be proven by mere
+instantiation and requires an inductive argument, which `inhabitationTest`
+currently isn't equipped to do.
+
+In order to prevent endless instantiation attempts in @inhabitationTest@, we
+use the fuel as an upper bound such attempts.
+
+The same problem occurs with recursive newtypes, like in the following code:
+
+ newtype Chasm = MkChasm Chasm
+ gazeIntoTheChasm :: Chasm -> a
+ gazeIntoTheChasm x = case x of {} -- Erroneously warned as non-exhaustive
+
+So this limitation is somewhat understandable.
+
+Note that even with this recursion detection, there is still a possibility that
+`inhabitationTest` can run in exponential time in the amount of fuel. Consider
+the following data type:
+
+ data T = MkT !T !T !T
+
+If we try to instantiate each of its fields, that will require us to once again
+check if `MkT` is inhabitable in each of those three fields, which in turn will
+require us to check if `MkT` is inhabitable again... As you can see, the
+branching factor adds up quickly, and if the initial fuel is, say,
+100, then the inhabiation test will effectively take forever.
+
+To mitigate this, we check the branching factor every time we are about to do
+inhabitation testing in 'instCon'. If the branching factor exceeds 1
+(i.e., if there is potential for exponential runtime), then we limit the
+maximum recursion depth to 1 to mitigate the problem. If the branching factor
+is exactly 1 (i.e., we have a linear chain instead of a tree), then it's okay
+to stick with a larger maximum recursion depth.
+
+In #17977 we saw that the defaultRecTcMaxBound (100 at the time of writing) was
+too large and had detrimental effect on performance of the coverage checker.
+Given that we only commit to a best effort anyway, we decided to substantially
+decrement the fuel to 4, at the cost of precision in some edge cases
+like
+
+ data Nat = Z | S Nat
+ data Down :: Nat -> Type where
+ Down :: !(Down n) -> Down (S n)
+ f :: Down (S (S (S (S (S Z))))) -> ()
+ f x = case x of {}
+
+Since the coverage won't bother to instantiate Down 4 levels deep to see that it
+is in fact uninhabited, it will emit a inexhaustivity warning for the case.
+
+Note [DataCons that are definitely inhabitable]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Another microoptimization applies to data types like this one:
+
+ data S a = S ![a] !T
+
+Even though there is a strict field of type [a], it's quite silly to call
+'instCon' on it, since it's "obvious" that it is inhabitable. To make this
+intuition formal, we say that a DataCon C is definitely inhabitable (DI) if:
+
+ 1. C has no equality constraints (since they might be unsatisfiable)
+ 2. C has no strict arguments (since they might be uninhabitable)
+ 3. C has no unlifted argument types (since they might be uninhabitable)
+
+It's relatively cheap to check if a DataCon is DI, so before we call 'instCon'
+on a constructor of a COMPLETE set, we filter out all of the DI ones.
+
+This fast path shaves down -7% allocations for PmSeriesG, for example.
+-}
+
+--------------------------------------
+-- * Generating inhabitants of a Nabla
+--
+-- This is important for warnings. Roughly corresponds to G in Figure 6 of the
+-- LYG paper, with a few tweaks for better warning messages.
+
+-- | @generateInhabitingPatterns vs n nabla@ returns a list of at most @n@ (but
+-- perhaps empty) refinements of @nabla@ that represent inhabited patterns.
+-- Negative information is only retained if literals are involved or for
+-- recursive GADTs.
+generateInhabitingPatterns :: [Id] -> Int -> Nabla -> DsM [Nabla]
+-- See Note [Why inhabitationTest doesn't call generateInhabitingPatterns]
+generateInhabitingPatterns _ 0 _ = pure []
+generateInhabitingPatterns [] _ nabla = pure [nabla]
+generateInhabitingPatterns (x:xs) n nabla = do
+ tracePm "generateInhabitingPatterns" (ppr n <+> ppr (x:xs) $$ ppr nabla)
+ let VI _ pos neg _ _ = lookupVarInfo (nabla_tm_st nabla) x
+ case pos of
+ _:_ -> do
+ -- All solutions must be valid at once. Try to find candidates for their
+ -- fields. Example:
+ -- f x@(Just _) True = case x of SomePatSyn _ -> ()
+ -- after this clause, we want to report that
+ -- * @f Nothing _@ is uncovered
+ -- * @f x False@ is uncovered
+ -- where @x@ will have two possibly compatible solutions, @Just y@ for
+ -- some @y@ and @SomePatSyn z@ for some @z@. We must find evidence for @y@
+ -- and @z@ that is valid at the same time. These constitute arg_vas below.
+ let arg_vas = concatMap paca_ids pos
+ generateInhabitingPatterns (arg_vas ++ xs) n nabla
+ []
+ -- When there are literals involved, just print negative info
+ -- instead of listing missed constructors
+ | notNull [ l | PmAltLit l <- pmAltConSetElems neg ]
+ -> generateInhabitingPatterns xs n nabla
+ [] -> try_instantiate x xs n nabla
+ where
+ -- | Tries to instantiate a variable by possibly following the chain of
+ -- newtypes and then instantiating to all ConLikes of the wrapped type's
+ -- minimal residual COMPLETE set.
+ try_instantiate :: Id -> [Id] -> Int -> Nabla -> DsM [Nabla]
+ -- Convention: x binds the outer constructor in the chain, y the inner one.
+ try_instantiate x xs n nabla = do
+ (_src_ty, dcs, rep_ty) <- tntrGuts <$> pmTopNormaliseType (nabla_ty_st nabla) (idType x)
+ mb_stuff <- runMaybeT $ instantiate_newtype_chain x nabla dcs
+ case mb_stuff of
+ Nothing -> pure []
+ Just (y, newty_nabla) -> do
+ let vi = lookupVarInfo (nabla_tm_st newty_nabla) y
+ env <- dsGetFamInstEnvs
+ rcm <- case splitReprTyConApp_maybe env rep_ty of
+ Just (tc, _, _) -> addTyConMatches tc (vi_rcm vi)
+ Nothing -> addCompleteMatches (vi_rcm vi)
+
+ -- Test all COMPLETE sets for inhabitants (n inhs at max). Take care of ⊥.
+ clss <- pickApplicableCompleteSets rep_ty rcm
+ case NE.nonEmpty (uniqDSetToList <$> clss) of
+ Nothing ->
+ -- No COMPLETE sets ==> inhabited
+ generateInhabitingPatterns xs n newty_nabla
+ Just clss -> do
+ -- Try each COMPLETE set, pick the one with the smallest number of
+ -- inhabitants
+ nablass' <- forM clss (instantiate_cons y rep_ty xs n newty_nabla)
+ let nablas' = minimumBy (comparing length) nablass'
+ if null nablas' && vi_bot vi /= IsNotBot
+ then generateInhabitingPatterns xs n newty_nabla -- bot is still possible. Display a wildcard!
+ else pure nablas'
+
+ -- | Instantiates a chain of newtypes, beginning at @x@.
+ -- Turns @x nabla [T,U,V]@ to @(y, nabla')@, where @nabla'@ we has the fact
+ -- @x ~ T (U (V y))@.
+ instantiate_newtype_chain :: Id -> Nabla -> [(Type, DataCon, Type)] -> MaybeT DsM (Id, Nabla)
+ instantiate_newtype_chain x nabla [] = pure (x, nabla)
+ instantiate_newtype_chain x nabla ((_ty, dc, arg_ty):dcs) = do
+ y <- lift $ mkPmId arg_ty
+ -- Newtypes don't have existentials (yet?!), so passing an empty
+ -- list as ex_tvs.
+ nabla' <- addConCt nabla x (PmAltConLike (RealDataCon dc)) [] [y]
+ instantiate_newtype_chain y nabla' dcs
+
+ instantiate_cons :: Id -> Type -> [Id] -> Int -> Nabla -> [ConLike] -> DsM [Nabla]
+ instantiate_cons _ _ _ _ _ [] = pure []
+ instantiate_cons _ _ _ 0 _ _ = pure []
+ instantiate_cons _ ty xs n nabla _
+ -- We don't want to expose users to GHC-specific constructors for Int etc.
+ | fmap (isTyConTriviallyInhabited . fst) (splitTyConApp_maybe ty) == Just True
+ = generateInhabitingPatterns xs n nabla
+ instantiate_cons x ty xs n nabla (cl:cls) = do
+ -- The following line is where we call out to the inhabitationTest!
+ mb_nabla <- runMaybeT $ instCon 4 nabla x cl
+ tracePm "instantiate_cons" (vcat [ ppr x <+> dcolon <+> ppr (idType x)
+ , ppr ty
+ , ppr cl
+ , ppr nabla
+ , ppr mb_nabla
+ , ppr n ])
+ con_nablas <- case mb_nabla of
+ Nothing -> pure []
+ -- NB: We don't prepend arg_vars as we don't have any evidence on
+ -- them and we only want to split once on a data type. They are
+ -- inhabited, otherwise the inhabitation test would have refuted.
+ Just nabla' -> generateInhabitingPatterns xs n nabla'
+ other_cons_nablas <- instantiate_cons x ty xs (n - length con_nablas) nabla cls
+ pure (con_nablas ++ other_cons_nablas)
+
+pickApplicableCompleteSets :: Type -> ResidualCompleteMatches -> DsM [ConLikeSet]
+pickApplicableCompleteSets ty rcm = do
+ env <- dsGetFamInstEnvs
+ pure $ filter (all (is_valid env) . uniqDSetToList) (getRcm rcm)
+ where
+ is_valid :: FamInstEnvs -> ConLike -> Bool
+ is_valid env cl = isJust (guessConLikeUnivTyArgsFromResTy env ty cl)
+
+{- Note [Why inhabitationTest doesn't call generateInhabitingPatterns]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Why can't we define `inhabitationTest` (IT) in terms of
+`generateInhabitingPatterns` (GIP) as
+
+ inhabitationTest nabla = do
+ nablas <- lift $ generateInhabitingPatterns all_variables 1 nabla
+ guard (notNull nablas)
+
+There are a few technical reasons, like the lack of a fuel-tracking approach
+to stay decidable, that could be overcome. But the nail in the coffin is
+performance: In order to provide good warning messages, GIP commits to *one*
+COMPLETE set, and goes through some hoops to find the minimal one. This implies
+it has to look at *all* constructors in the residual COMPLETE matches and see if
+they match, if only to filter out ill-typed COMPLETE sets
+(see Note [Implementation of COMPLETE pragmas]). That is untractable for an
+efficient IT on huge enumerations.
+
+But we still need GIP to produce the Nablas as proxies for
+uncovered patterns that we display warnings for. It's fine to pay this price
+once at the end, but IT is called far more often than that.
-}
diff --git a/compiler/GHC/HsToCore/PmCheck/Ppr.hs b/compiler/GHC/HsToCore/PmCheck/Ppr.hs
index 557fd08c43..94ea9d6361 100644
--- a/compiler/GHC/HsToCore/PmCheck/Ppr.hs
+++ b/compiler/GHC/HsToCore/PmCheck/Ppr.hs
@@ -146,8 +146,8 @@ pprPmVar :: PprPrec -> Id -> PmPprM SDoc
pprPmVar prec x = do
nabla <- ask
case lookupSolution nabla x of
- Just (alt, _tvs, args) -> pprPmAltCon prec alt args
- Nothing -> fromMaybe typed_wildcard <$> checkRefuts x
+ Just (PACA alt _tvs args) -> pprPmAltCon prec alt args
+ Nothing -> fromMaybe typed_wildcard <$> checkRefuts x
where
-- if we have no info about the parameter and would just print a
-- wildcard, also show its type.
@@ -206,7 +206,7 @@ pmExprAsList :: Nabla -> PmAltCon -> [Id] -> Maybe PmExprList
pmExprAsList nabla = go_con []
where
go_var rev_pref x
- | Just (alt, _tvs, args) <- lookupSolution nabla x
+ | Just (PACA alt _tvs args) <- lookupSolution nabla x
= go_con rev_pref alt args
go_var rev_pref x
| Just pref <- nonEmpty (reverse rev_pref)
diff --git a/compiler/GHC/HsToCore/PmCheck/Types.hs b/compiler/GHC/HsToCore/PmCheck/Types.hs
index 4602b89611..3577a8d8ad 100644
--- a/compiler/GHC/HsToCore/PmCheck/Types.hs
+++ b/compiler/GHC/HsToCore/PmCheck/Types.hs
@@ -25,7 +25,7 @@ module GHC.HsToCore.PmCheck.Types (
pmLitAsStringLit, coreExprAsPmLit,
-- * Caching residual COMPLETE sets
- ConLikeSet, ResidualCompleteMatches(..), getRcm,
+ ConLikeSet, ResidualCompleteMatches(..), getRcm, isRcmInitialised,
-- * PmAltConSet
PmAltConSet, emptyPmAltConSet, isEmptyPmAltConSet, elemPmAltConSet,
@@ -33,11 +33,11 @@ module GHC.HsToCore.PmCheck.Types (
-- * A 'DIdEnv' where entries may be shared
Shared(..), SharedDIdEnv(..), emptySDIE, lookupSDIE, sameRepresentativeSDIE,
- setIndirectSDIE, setEntrySDIE, traverseSDIE,
+ setIndirectSDIE, setEntrySDIE, traverseSDIE, entriesSDIE,
-- * The pattern match oracle
- BotInfo(..), VarInfo(..), TmState(..), TyState(..), Nabla(..),
- Nablas(..), initNablas, liftNablasM
+ BotInfo(..), PmAltConApp(..), VarInfo(..), TmState(..), TyState(..),
+ Nabla(..), Nablas(..), initNablas, liftNablasM
) where
#include "HsVersions.h"
@@ -49,6 +49,7 @@ import GHC.Data.Bag
import GHC.Data.FastString
import GHC.Types.Id
import GHC.Types.Var.Env
+import GHC.Types.Var.Set
import GHC.Types.Unique.DSet
import GHC.Types.Unique.DFM
import GHC.Types.Name
@@ -437,6 +438,9 @@ data ResidualCompleteMatches
getRcm :: ResidualCompleteMatches -> [ConLikeSet]
getRcm (RCM vanilla pragmas) = maybeToList vanilla ++ fromMaybe [] pragmas
+isRcmInitialised :: ResidualCompleteMatches -> Bool
+isRcmInitialised (RCM vanilla pragmas) = isJust vanilla && isJust pragmas
+
instance Outputable ResidualCompleteMatches where
-- formats as "[{Nothing,Just},{P,Q}]"
ppr rcm = ppr (getRcm rcm)
@@ -485,6 +489,12 @@ setEntrySDIE :: SharedDIdEnv a -> Id -> a -> SharedDIdEnv a
setEntrySDIE sdie@(SDIE env) x a =
SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Entry a)
+entriesSDIE :: SharedDIdEnv a -> [a]
+entriesSDIE (SDIE env) = mapMaybe preview_entry (eltsUDFM env)
+ where
+ preview_entry (Entry e) = Just e
+ preview_entry _ = Nothing
+
traverseSDIE :: forall a b f. Applicative f => (a -> f b) -> SharedDIdEnv a -> f (SharedDIdEnv b)
traverseSDIE f = fmap (SDIE . listToUDFM_Directly) . traverse g . udfmToList . unSDIE
where
@@ -501,13 +511,6 @@ instance Outputable a => Outputable (Shared a) where
instance Outputable a => Outputable (SharedDIdEnv a) where
ppr (SDIE env) = ppr env
--- | See 'vi_bot'.
-data BotInfo
- = IsBot
- | IsNotBot
- | MaybeBot
- deriving Eq
-
-- | The term oracle state. Stores 'VarInfo' for encountered 'Id's. These
-- entries are possibly shared when we figure out that two variables must be
-- equal, thus represent the same set of values.
@@ -522,6 +525,9 @@ data TmState
-- ^ An environment for looking up whether we already encountered semantically
-- equivalent expressions that we want to represent by the same 'Id'
-- representative.
+ , ts_dirty :: !DIdSet
+ -- ^ Which 'VarInfo' needs to be checked for inhabitants because of new
+ -- negative constraints (e.g. @x ≁ ⊥@ or @x ≁ K@).
}
-- | Information about an 'Id'. Stores positive ('vi_pos') facts, like @x ~ Just 42@,
@@ -532,11 +538,11 @@ data TmState
-- Subject to Note [The Pos/Neg invariant] in "GHC.HsToCore.PmCheck.Oracle".
data VarInfo
= VI
- { vi_ty :: !Type
- -- ^ The type of the variable. Important for rejecting possible GADT
- -- constructors or incompatible pattern synonyms (@Just42 :: Maybe Int@).
+ { vi_id :: !Id
+ -- ^ The 'Id' in question. Important for adding new constraints relative to
+ -- this 'VarInfo' when we don't easily have the 'Id' available.
- , vi_pos :: ![(PmAltCon, [TyVar], [Id])]
+ , vi_pos :: ![PmAltConApp]
-- ^ Positive info: 'PmAltCon' apps it is (i.e. @x ~ [Just y, PatSyn z]@), all
-- at the same time (i.e. conjunctive). We need a list because of nested
-- pattern matches involving pattern synonym
@@ -576,40 +582,76 @@ data VarInfo
-- to recognise completion of a COMPLETE set efficiently for large enums.
}
+data PmAltConApp
+ = PACA
+ { paca_con :: !PmAltCon
+ , paca_tvs :: ![TyVar]
+ , paca_ids :: ![Id]
+ }
+
+-- | See 'vi_bot'.
+data BotInfo
+ = IsBot
+ | IsNotBot
+ | MaybeBot
+ deriving Eq
+
+instance Outputable PmAltConApp where
+ ppr PACA{paca_con = con, paca_tvs = tvs, paca_ids = ids} =
+ hsep (ppr con : map ((char '@' <>) . ppr) tvs ++ map ppr ids)
+
instance Outputable BotInfo where
- ppr MaybeBot = empty
+ ppr MaybeBot = underscore
ppr IsBot = text "~⊥"
ppr IsNotBot = text "≁⊥"
-- | Not user-facing.
instance Outputable TmState where
- ppr (TmSt state reps) = ppr state $$ ppr reps
+ ppr (TmSt state reps dirty) = ppr state $$ ppr reps $$ ppr dirty
-- | Not user-facing.
instance Outputable VarInfo where
- ppr (VI ty pos neg bot cache)
- = braces (hcat (punctuate comma [ppr ty, ppr pos, ppr neg, ppr bot, ppr cache]))
+ ppr (VI x pos neg bot cache)
+ = braces (hcat (punctuate comma [pp_x, pp_pos, pp_neg, ppr bot, pp_cache]))
+ where
+ pp_x = ppr x <> dcolon <> ppr (idType x)
+ pp_pos
+ | [] <- pos = underscore
+ | [p] <- pos = char '~' <> ppr p -- suppress outer [_] if singleton
+ | otherwise = char '~' <> ppr pos
+ pp_neg
+ | isEmptyPmAltConSet neg = underscore
+ | otherwise = char '≁' <> ppr neg
+ pp_cache
+ | RCM Nothing Nothing <- cache = underscore
+ | otherwise = ppr cache
-- | Initial state of the term oracle.
initTmState :: TmState
-initTmState = TmSt emptySDIE emptyCoreMap
+initTmState = TmSt emptySDIE emptyCoreMap emptyDVarSet
--- | The type oracle state. A poor man's 'GHC.Tc.Solver.Monad.InsertSet': The invariant is
--- that all constraints in there are mutually compatible.
-newtype TyState = TySt InertSet
+-- | The type oracle state. An 'GHC.Tc.Solver.Monad.InsertSet' that we
+-- incrementally add local type constraints to, together with a sequence
+-- number that counts the number of times we extended it with new facts.
+data TyState = TySt { ty_st_n :: !Int, ty_st_inert :: !InertSet }
-- | Not user-facing.
instance Outputable TyState where
- ppr (TySt inert) = ppr inert
+ ppr (TySt n inert) = ppr n <+> ppr inert
initTyState :: TyState
-initTyState = TySt emptyInert
+initTyState = TySt 0 emptyInert
-- | A normalised refinement type ∇ (\"nabla\"), comprised of an inert set of
-- canonical (i.e. mutually compatible) term and type constraints that form the
-- refinement type's predicate.
-data Nabla = MkNabla { nabla_ty_st :: TyState -- Type oracle; things like a~Int
- , nabla_tm_st :: TmState } -- Term oracle; things like x~Nothing
+data Nabla
+ = MkNabla
+ { nabla_ty_st :: !TyState
+ -- ^ Type oracle; things like a~Int
+ , nabla_tm_st :: !TmState
+ -- ^ Term oracle; things like x~Nothing
+ }
-- | An initial nabla that is always satisfiable
initNabla :: Nabla
diff --git a/compiler/GHC/HsToCore/PmCheck/Types.hs-boot b/compiler/GHC/HsToCore/PmCheck/Types.hs-boot
deleted file mode 100644
index a0505bce9d..0000000000
--- a/compiler/GHC/HsToCore/PmCheck/Types.hs-boot
+++ /dev/null
@@ -1,9 +0,0 @@
-module GHC.HsToCore.PmCheck.Types where
-
-import GHC.Data.Bag
-
-data Nabla
-
-newtype Nablas = MkNablas (Bag Nabla)
-
-initNablas :: Nablas
diff --git a/compiler/GHC/HsToCore/Types.hs b/compiler/GHC/HsToCore/Types.hs
index b99970f55f..d6fd94e723 100644
--- a/compiler/GHC/HsToCore/Types.hs
+++ b/compiler/GHC/HsToCore/Types.hs
@@ -14,7 +14,7 @@ import GHC.Types.SrcLoc
import GHC.Types.Var
import GHC.Hs (LForeignDecl, HsExpr, GhcTc)
import GHC.Tc.Types (TcRnIf, IfGblEnv, IfLclEnv, CompleteMatches)
-import {-# SOURCE #-} GHC.HsToCore.PmCheck.Types (Nablas)
+import GHC.HsToCore.PmCheck.Types (Nablas)
import GHC.Core (CoreExpr)
import GHC.Core.FamInstEnv
import GHC.Utils.Error
diff --git a/compiler/GHC/Tc/Gen/Expr.hs b/compiler/GHC/Tc/Gen/Expr.hs
index 81b0c6792b..9870c36ff5 100644
--- a/compiler/GHC/Tc/Gen/Expr.hs
+++ b/compiler/GHC/Tc/Gen/Expr.hs
@@ -1229,7 +1229,9 @@ instance OutputableBndrId id => Outputable (HsExprArg id) where
ppr (HsEPar _) = text "HsEPar"
ppr (HsEWrap w) = case ghcPass @id of
GhcTc -> text "HsEWrap" <+> ppr w
+#if __GLASGOW_HASKELL__ <= 900
_ -> empty
+#endif
type family XExprTypeArg id where
XExprTypeArg 'Parsed = NoExtField