summaryrefslogtreecommitdiff
path: root/compiler/deSugar/Check.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/deSugar/Check.hs')
-rw-r--r--compiler/deSugar/Check.hs2316
1 files changed, 629 insertions, 1687 deletions
diff --git a/compiler/deSugar/Check.hs b/compiler/deSugar/Check.hs
index 602c125f67..3a1515d0cc 100644
--- a/compiler/deSugar/Check.hs
+++ b/compiler/deSugar/Check.hs
@@ -11,21 +11,28 @@ Pattern Matching Coverage Checking.
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE LambdaCase #-}
module Check (
-- Checking and printing
- checkSingle, checkMatches, checkGuardMatches, isAnyPmCheckEnabled,
+ checkSingle, checkMatches, checkGuardMatches,
+ needToRunPmCheck, isMatchContextPmChecked,
-- See Note [Type and Term Equality Propagation]
- genCaseTmCs1, genCaseTmCs2
+ addTyCsDs, addScrutTmCs, addPatTmCs
) where
#include "HsVersions.h"
import GhcPrelude
-import TmOracle
+import PmExpr
+import PmOracle
import PmPpr
+import BasicTypes (Origin, isGenerated)
+import CoreSyn (CoreExpr, Expr(Var))
+import CoreUtils (exprType)
+import FastString (unpackFS)
import Unify( tcMatchTy )
import DynFlags
import HsSyn
@@ -33,42 +40,37 @@ import TcHsSyn
import Id
import ConLike
import Name
-import FamInstEnv
-import TysPrim (tYPETyCon)
+import FamInst
import TysWiredIn
import TyCon
import SrcLoc
import Util
import Outputable
-import FastString
import DataCon
import PatSyn
import HscTypes (CompleteMatch(..))
import BasicTypes (Boxity(..))
+import Var (EvVar)
+import {-# SOURCE #-} DsExpr (dsExpr, dsLExpr)
+import MatchLit (dsLit, dsOverLit)
import DsMonad
-import TcSimplify (tcCheckSatisfiability)
-import TcType (isStringTy)
import Bag
-import ErrUtils
-import Var (EvVar)
import TyCoRep
import Type
-import UniqSupply
import DsUtils (isTrueLHsExpr)
+import Maybes (isJust, expectJust)
import qualified GHC.LanguageExtensions as LangExt
import Data.List (find)
-import Data.Maybe (catMaybes, isJust, fromMaybe)
-import Control.Monad (forM, when, forM_, zipWithM, filterM)
+import Control.Monad (forM, when, forM_)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Maybe
import Coercion
import TcEvidence
-import TcSimplify (tcNormalise)
import IOEnv
import qualified Data.Semigroup as Semi
-import ListT (ListT(..), fold, select)
-
{-
This module checks pattern matches for:
\begin{enumerate}
@@ -91,98 +93,44 @@ The algorithm is based on the paper:
%************************************************************************
-}
--- We use the non-determinism monad to apply the algorithm to several
--- possible sets of constructors. Users can specify complete sets of
--- constructors by using COMPLETE pragmas.
--- The algorithm only picks out constructor
--- sets deep in the bowels which makes a simpler `mapM` more difficult to
--- implement. The non-determinism is only used in one place, see the ConVar
--- case in `pmCheckHd`.
+data PmPat where
+ -- | For the arguments' meaning see 'HsPat.ConPatOut'.
+ PmCon :: { pm_con_con :: PmAltCon
+ , pm_con_arg_tys :: [Type]
+ , pm_con_tvs :: [TyVar]
+ , pm_con_args :: [PmPat] } -> PmPat
-type PmM a = ListT DsM a
+ PmVar :: { pm_var_id :: Id } -> PmPat
-liftD :: DsM a -> PmM a
-liftD m = ListT $ \sk fk -> m >>= \a -> sk a fk
+ PmGrd :: { pm_grd_pv :: PatVec -- ^ Always has 'patVecArity' 1.
+ , pm_grd_expr :: CoreExpr } -> PmPat
+ -- (PmGrd pat expr) matches expr against pat, binding the variables in pat
--- Pick the first match complete covered match or otherwise the "best" match.
--- The best match is the one with the least uncovered clauses, ties broken
--- by the number of inaccessible clauses followed by number of redundant
--- clauses.
---
--- This is specified in the
--- "Disambiguating between multiple ``COMPLETE`` pragmas" section of the
--- users' guide. If you update the implementation of this function, make sure
--- to update that section of the users' guide as well.
-getResult :: PmM PmResult -> DsM PmResult
-getResult ls
- = do { res <- fold ls goM (pure Nothing)
- ; case res of
- Nothing -> panic "getResult is empty"
- Just a -> return a }
- where
- goM :: PmResult -> DsM (Maybe PmResult) -> DsM (Maybe PmResult)
- goM mpm dpm = do { pmr <- dpm
- ; return $ Just $ go pmr mpm }
-
- -- Careful not to force unecessary results
- go :: Maybe PmResult -> PmResult -> PmResult
- go Nothing rs = rs
- go (Just old@(PmResult prov rs (UncoveredPatterns us) is)) new
- | null us && null rs && null is = old
- | otherwise =
- let PmResult prov' rs' (UncoveredPatterns us') is' = new
- in case compareLength us us'
- `mappend` (compareLength is is')
- `mappend` (compareLength rs rs')
- `mappend` (compare prov prov') of
- GT -> new
- EQ -> new
- LT -> old
- go (Just (PmResult _ _ (TypeOfUncovered _) _)) _new
- = panic "getResult: No inhabitation candidates"
-
-data PatTy = PAT | VA -- Used only as a kind, to index PmPat
-
--- The *arity* of a PatVec [p1,..,pn] is
--- the number of p1..pn that are not Guards
-
-data PmPat :: PatTy -> * where
- PmCon :: { pm_con_con :: ConLike
- , pm_con_arg_tys :: [Type]
- , pm_con_tvs :: [TyVar]
- , pm_con_dicts :: [EvVar]
- , pm_con_args :: [PmPat t] } -> PmPat t
- -- For PmCon arguments' meaning see @ConPatOut@ in hsSyn/HsPat.hs
- PmVar :: { pm_var_id :: Id } -> PmPat t
- PmLit :: { pm_lit_lit :: PmLit } -> PmPat t -- See Note [Literals in PmPat]
- PmNLit :: { pm_lit_id :: Id
- , pm_lit_not :: [PmLit] } -> PmPat 'VA
- PmGrd :: { pm_grd_pv :: PatVec
- , pm_grd_expr :: PmExpr } -> PmPat 'PAT
-- | A fake guard pattern (True <- _) used to represent cases we cannot handle.
- PmFake :: PmPat 'PAT
+ PmFake :: PmPat
-instance Outputable (PmPat a) where
- ppr = pprPmPatDebug
+-- | Should not be user-facing.
+instance Outputable PmPat where
+ ppr (PmCon alt _arg_tys _con_tvs con_args)
+ = cparen (notNull con_args) (hsep [ppr alt, hsep (map ppr con_args)])
+ ppr (PmVar vid) = ppr vid
+ ppr (PmGrd pv ge) = hsep (map ppr pv) <+> text "<-" <+> ppr ge
+ ppr PmFake = text "<PmFake>"
-- data T a where
-- MkT :: forall p q. (Eq p, Ord q) => p -> q -> T [p]
-- or MkT :: forall p q r. (Eq p, Ord q, [p] ~ r) => p -> q -> T r
-type Pattern = PmPat 'PAT -- ^ Patterns
-type ValAbs = PmPat 'VA -- ^ Value Abstractions
-
-type PatVec = [Pattern] -- ^ Pattern Vectors
-data ValVec = ValVec [ValAbs] Delta -- ^ Value Vector Abstractions
+-- | Pattern Vectors. The *arity* of a PatVec [p1,..,pn] is
+-- the number of p1..pn that are not Guards. See 'patternArity'.
+type PatVec = [PmPat]
+type ValVec = [Id] -- ^ Value Vector Abstractions
--- | Term and type constraints to accompany each value vector abstraction.
--- For efficiency, we store the term oracle state instead of the term
--- constraints. TODO: Do the same for the type constraints?
-data Delta = MkDelta { delta_ty_cs :: Bag EvVar
- , delta_tm_cs :: TmState }
-
-type ValSetAbs = [ValVec] -- ^ Value Set Abstractions
-type Uncovered = ValSetAbs
+-- | Each 'Delta' is proof (i.e., a model of the fact) that some values are not
+-- covered by a pattern match. E.g. @f Nothing = <rhs>@ might be given an
+-- uncovered set @[x :-> Just y]@ or @[x /= Nothing]@, where @x@ is the variable
+-- matching against @f@'s first argument.
+type Uncovered = [Delta]
-- Instead of keeping the whole sets in memory, we keep a boolean for both the
-- covered and the divergent set (we store the uncovered set though, since we
@@ -199,8 +147,7 @@ data Covered = Covered | NotCovered
deriving Show
instance Outputable Covered where
- ppr (Covered) = text "Covered"
- ppr (NotCovered) = text "NotCovered"
+ ppr = text . show
-- Like the or monoid for booleans
-- Covered = True, Uncovered = False
@@ -217,8 +164,7 @@ data Diverged = Diverged | NotDiverged
deriving Show
instance Outputable Diverged where
- ppr Diverged = text "Diverged"
- ppr NotDiverged = text "NotDiverged"
+ ppr = text . show
instance Semi.Semigroup Diverged where
Diverged <> _ = Diverged
@@ -229,55 +175,36 @@ instance Monoid Diverged where
mempty = NotDiverged
mappend = (Semi.<>)
--- | When we learned that a given match group is complete
-data Provenance =
- FromBuiltin -- ^ From the original definition of the type
- -- constructor.
- | FromComplete -- ^ From a user-provided @COMPLETE@ pragma
- deriving (Show, Eq, Ord)
-
-instance Outputable Provenance where
- ppr = text . show
-
-instance Semi.Semigroup Provenance where
- FromComplete <> _ = FromComplete
- _ <> FromComplete = FromComplete
- _ <> _ = FromBuiltin
-
-instance Monoid Provenance where
- mempty = FromBuiltin
- mappend = (Semi.<>)
-
+-- | A triple <C,U,D> of covered, uncovered, and divergent sets.
data PartialResult = PartialResult {
- presultProvenance :: Provenance
- -- keep track of provenance because we don't want
- -- to warn about redundant matches if the result
- -- is contaminated with a COMPLETE pragma
- , presultCovered :: Covered
+ presultCovered :: Covered
, presultUncovered :: Uncovered
, presultDivergent :: Diverged }
-instance Outputable PartialResult where
- ppr (PartialResult prov c vsa d)
- = text "PartialResult" <+> ppr prov <+> ppr c
- <+> ppr d <+> ppr vsa
+emptyPartialResult :: PartialResult
+emptyPartialResult = PartialResult { presultUncovered = mempty
+ , presultCovered = mempty
+ , presultDivergent = mempty }
+combinePartialResults :: PartialResult -> PartialResult -> PartialResult
+combinePartialResults (PartialResult cs1 vsa1 ds1) (PartialResult cs2 vsa2 ds2)
+ = PartialResult (cs1 Semi.<> cs2)
+ (vsa1 Semi.<> vsa2)
+ (ds1 Semi.<> ds2)
-instance Semi.Semigroup PartialResult where
- (PartialResult prov1 cs1 vsa1 ds1)
- <> (PartialResult prov2 cs2 vsa2 ds2)
- = PartialResult (prov1 Semi.<> prov2)
- (cs1 Semi.<> cs2)
- (vsa1 Semi.<> vsa2)
- (ds1 Semi.<> ds2)
+instance Outputable PartialResult where
+ ppr (PartialResult c vsa d)
+ = hang (text "PartialResult" <+> ppr c <+> ppr d) 2 (ppr_vsa vsa)
+ where
+ ppr_vsa = braces . fsep . punctuate comma . map ppr
+instance Semi.Semigroup PartialResult where
+ (<>) = combinePartialResults
instance Monoid PartialResult where
- mempty = PartialResult mempty mempty [] mempty
+ mempty = emptyPartialResult
mappend = (Semi.<>)
--- newtype ChoiceOf a = ChoiceOf [a]
-
-- | Pattern check result
--
-- * Redundant clauses
@@ -291,15 +218,13 @@ instance Monoid PartialResult where
--
data PmResult =
PmResult {
- pmresultProvenance :: Provenance
- , pmresultRedundant :: [Located [LPat GhcTc]]
+ pmresultRedundant :: [Located [LPat GhcTc]]
, pmresultUncovered :: UncoveredCandidates
, pmresultInaccessible :: [Located [LPat GhcTc]] }
instance Outputable PmResult where
ppr pmr = hang (text "PmResult") 2 $ vcat
- [ text "pmresultProvenance" <+> ppr (pmresultProvenance pmr)
- , text "pmresultRedundant" <+> ppr (pmresultRedundant pmr)
+ [ text "pmresultRedundant" <+> ppr (pmresultRedundant pmr)
, text "pmresultUncovered" <+> ppr (pmresultUncovered pmr)
, text "pmresultInaccessible" <+> ppr (pmresultInaccessible pmr)
]
@@ -315,21 +240,13 @@ instance Outputable PmResult where
-- but we don't want to issue just a wildcard as missing. Instead, we print a
-- type annotated wildcard, so that the user knows what kind of patterns is
-- expected (e.g. (_ :: Int), or (_ :: F Int), where F Int does not reduce).
-data UncoveredCandidates = UncoveredPatterns Uncovered
+data UncoveredCandidates = UncoveredPatterns [Id] [Delta]
| TypeOfUncovered Type
instance Outputable UncoveredCandidates where
- ppr (UncoveredPatterns uc) = text "UnPat" <+> ppr uc
+ ppr (UncoveredPatterns vva deltas) = text "UnPat" <+> ppr vva $$ ppr deltas
ppr (TypeOfUncovered ty) = text "UnTy" <+> ppr ty
--- | The empty pattern check result
-emptyPmResult :: PmResult
-emptyPmResult = PmResult FromBuiltin [] (UncoveredPatterns []) []
-
--- | Non-exhaustive empty case with unknown/trivial inhabitants
-uncoveredWithTy :: Type -> PmResult
-uncoveredWithTy ty = PmResult FromBuiltin [] (TypeOfUncovered ty) []
-
{-
%************************************************************************
%* *
@@ -341,27 +258,28 @@ uncoveredWithTy ty = PmResult FromBuiltin [] (TypeOfUncovered ty) []
-- | Check a single pattern binding (let)
checkSingle :: DynFlags -> DsMatchContext -> Id -> Pat GhcTc -> DsM ()
checkSingle dflags ctxt@(DsMatchContext _ locn) var p = do
- tracePmD "checkSingle" (vcat [ppr ctxt, ppr var, ppr p])
- mb_pm_res <- tryM (getResult (checkSingle' locn var p))
+ tracePm "checkSingle" (vcat [ppr ctxt, ppr var, ppr p])
+ mb_pm_res <- tryM (checkSingle' locn var p)
case mb_pm_res of
Left _ -> warnPmIters dflags ctxt
Right res -> dsPmWarn dflags ctxt res
-- | Check a single pattern binding (let)
-checkSingle' :: SrcSpan -> Id -> Pat GhcTc -> PmM PmResult
+checkSingle' :: SrcSpan -> Id -> Pat GhcTc -> DsM PmResult
checkSingle' locn var p = do
- liftD resetPmIterDs -- set the iter-no to zero
- fam_insts <- liftD dsGetFamInstEnvs
- clause <- liftD $ translatePat fam_insts p
- missing <- mkInitialUncovered [var]
- tracePm "checkSingle': missing" (vcat (map pprValVecDebug missing))
- -- no guards
- PartialResult prov cs us ds <- runMany (pmcheckI clause []) missing
- let us' = UncoveredPatterns us
+ resetPmIterDs -- set the iter-no to zero
+ fam_insts <- dsGetFamInstEnvs
+ clause <- translatePat fam_insts p
+ missing <- getPmDelta
+ tracePm "checkSingle': missing" (ppr missing)
+ PartialResult cs us ds <- pmcheckI clause [] [var] 1 missing
+ dflags <- getDynFlags
+ us' <- getNFirstUncovered [var] (maxUncoveredPatterns dflags + 1) us
+ let uc = UncoveredPatterns [var] us'
return $ case (cs,ds) of
- (Covered, _ ) -> PmResult prov [] us' [] -- useful
- (NotCovered, NotDiverged) -> PmResult prov m us' [] -- redundant
- (NotCovered, Diverged ) -> PmResult prov [] us' m -- inaccessible rhs
+ (Covered, _ ) -> PmResult [] uc [] -- useful
+ (NotCovered, NotDiverged) -> PmResult m uc [] -- redundant
+ (NotCovered, Diverged ) -> PmResult [] uc m -- inaccessible rhs
where m = [cL locn [cL locn p]]
-- | Exhaustive for guard matches, is used for guards in pattern bindings and
@@ -385,14 +303,14 @@ checkGuardMatches _ (XGRHSs nec) = noExtCon nec
checkMatches :: DynFlags -> DsMatchContext
-> [Id] -> [LMatch GhcTc (LHsExpr GhcTc)] -> DsM ()
checkMatches dflags ctxt vars matches = do
- tracePmD "checkMatches" (hang (vcat [ppr ctxt
+ tracePm "checkMatches" (hang (vcat [ppr ctxt
, ppr vars
, text "Matches:"])
2
(vcat (map ppr matches)))
- mb_pm_res <- tryM $ getResult $ case matches of
+ mb_pm_res <- tryM $ case matches of
-- Check EmptyCase separately
- -- See Note [Checking EmptyCase Expressions]
+ -- See Note [Checking EmptyCase Expressions] in PmOracle
[] | [var] <- vars -> checkEmptyCase' var
_normal_match -> checkMatches' vars matches
case mb_pm_res of
@@ -401,42 +319,42 @@ checkMatches dflags ctxt vars matches = do
-- | Check a matchgroup (case, functions, etc.). To be called on a non-empty
-- list of matches. For empty case expressions, use checkEmptyCase' instead.
-checkMatches' :: [Id] -> [LMatch GhcTc (LHsExpr GhcTc)] -> PmM PmResult
+checkMatches' :: [Id] -> [LMatch GhcTc (LHsExpr GhcTc)] -> DsM PmResult
checkMatches' vars matches
| null matches = panic "checkMatches': EmptyCase"
| otherwise = do
- liftD resetPmIterDs -- set the iter-no to zero
- missing <- mkInitialUncovered vars
- tracePm "checkMatches': missing" (vcat (map pprValVecDebug missing))
- (prov, rs,us,ds) <- go matches missing
+ resetPmIterDs -- set the iter-no to zero
+ missing <- getPmDelta
+ tracePm "checkMatches': missing" (ppr missing)
+ (rs,us,ds) <- go matches [missing]
+ dflags <- getDynFlags
+ us' <- getNFirstUncovered vars (maxUncoveredPatterns dflags + 1) us
+ let up = UncoveredPatterns vars us'
return $ PmResult {
- pmresultProvenance = prov
- , pmresultRedundant = map hsLMatchToLPats rs
- , pmresultUncovered = UncoveredPatterns us
+ pmresultRedundant = map hsLMatchToLPats rs
+ , pmresultUncovered = up
, pmresultInaccessible = map hsLMatchToLPats ds }
where
go :: [LMatch GhcTc (LHsExpr GhcTc)] -> Uncovered
- -> PmM (Provenance
- , [LMatch GhcTc (LHsExpr GhcTc)]
+ -> DsM ( [LMatch GhcTc (LHsExpr GhcTc)]
, Uncovered
, [LMatch GhcTc (LHsExpr GhcTc)])
- go [] missing = return (mempty, [], missing, [])
+ go [] missing = return ([], missing, [])
go (m:ms) missing = do
- tracePm "checkMatches': go" (ppr m $$ ppr missing)
- fam_insts <- liftD dsGetFamInstEnvs
- (clause, guards) <- liftD $ translateMatch fam_insts m
- r@(PartialResult prov cs missing' ds)
- <- runMany (pmcheckI clause guards) missing
+ tracePm "checkMatches': go" (ppr m)
+ fam_insts <- dsGetFamInstEnvs
+ (clause, guards) <- translateMatch fam_insts m
+ r@(PartialResult cs missing' ds)
+ <- runMany (pmcheckI clause guards vars (length missing)) missing
tracePm "checkMatches': go: res" (ppr r)
- (ms_prov, rs, final_u, is) <- go ms missing'
- let final_prov = prov `mappend` ms_prov
+ (rs, final_u, is) <- go ms missing'
return $ case (cs, ds) of
-- useful
- (Covered, _ ) -> (final_prov, rs, final_u, is)
+ (Covered, _ ) -> (rs, final_u, is)
-- redundant
- (NotCovered, NotDiverged) -> (final_prov, m:rs, final_u,is)
+ (NotCovered, NotDiverged) -> (m:rs, final_u,is)
-- inaccessible
- (NotCovered, Diverged ) -> (final_prov, rs, final_u, m:is)
+ (NotCovered, Diverged ) -> (rs, final_u, m:is)
hsLMatchToLPats :: LMatch id body -> Located [LPat id]
hsLMatchToLPats (dL->L l (Match { m_pats = pats })) = cL l pats
@@ -444,472 +362,59 @@ checkMatches' vars matches
-- | Check an empty case expression. Since there are no clauses to process, we
-- only compute the uncovered set. See Note [Checking EmptyCase Expressions]
--- for details.
-checkEmptyCase' :: Id -> PmM PmResult
-checkEmptyCase' var = do
- tm_ty_css <- pmInitialTmTyCs
- mb_candidates <- inhabitationCandidates (delta_ty_cs tm_ty_css) (idType var)
- case mb_candidates of
+-- in "PmOracle" for details.
+checkEmptyCase' :: Id -> DsM PmResult
+checkEmptyCase' x = do
+ delta <- getPmDelta
+ us <- inhabitants delta (idType x) >>= \case
-- Inhabitation checking failed / the type is trivially inhabited
- Left ty -> return (uncoveredWithTy ty)
-
- -- A list of inhabitant candidates is available: Check for each
- -- one for the satisfiability of the constraints it gives rise to.
- Right (_, candidates) -> do
- missing_m <- flip mapMaybeM candidates $
- \InhabitationCandidate{ ic_val_abs = va, ic_tm_ct = tm_ct
- , ic_ty_cs = ty_cs
- , ic_strict_arg_tys = strict_arg_tys } -> do
- mb_sat <- pmIsSatisfiable tm_ty_css tm_ct ty_cs strict_arg_tys
- pure $ fmap (ValVec [va]) mb_sat
- return $ if null missing_m
- then emptyPmResult
- else PmResult FromBuiltin [] (UncoveredPatterns missing_m) []
-
--- | Returns 'True' if the argument 'Type' is a fully saturated application of
--- a closed type constructor.
---
--- Closed type constructors are those with a fixed right hand side, as
--- opposed to e.g. associated types. These are of particular interest for
--- pattern-match coverage checking, because GHC can exhaustively consider all
--- possible forms that values of a closed type can take on.
---
--- Note that this function is intended to be used to check types of value-level
--- patterns, so as a consequence, the 'Type' supplied as an argument to this
--- function should be of kind @Type@.
-pmIsClosedType :: Type -> Bool
-pmIsClosedType ty
- = case splitTyConApp_maybe ty of
- Just (tc, ty_args)
- | is_algebraic_like tc && not (isFamilyTyCon tc)
- -> ASSERT2( ty_args `lengthIs` tyConArity tc, ppr ty ) True
- _other -> False
- where
- -- This returns True for TyCons which /act like/ algebraic types.
- -- (See "Type#type_classification" for what an algebraic type is.)
- --
- -- This is qualified with \"like\" because of a particular special
- -- case: TYPE (the underlyind kind behind Type, among others). TYPE
- -- is conceptually a datatype (and thus algebraic), but in practice it is
- -- a primitive builtin type, so we must check for it specially.
- --
- -- NB: it makes sense to think of TYPE as a closed type in a value-level,
- -- pattern-matching context. However, at the kind level, TYPE is certainly
- -- not closed! Since this function is specifically tailored towards pattern
- -- matching, however, it's OK to label TYPE as closed.
- is_algebraic_like :: TyCon -> Bool
- is_algebraic_like tc = isAlgTyCon tc || tc == tYPETyCon
-
-pmTopNormaliseType_maybe :: FamInstEnvs -> Bag EvVar -> Type
- -> PmM (Maybe (Type, [DataCon], Type))
--- ^ Get rid of *outermost* (or toplevel)
--- * type function redex
--- * data family redex
--- * newtypes
---
--- Behaves exactly like `topNormaliseType_maybe`, but instead of returning a
--- coercion, it returns useful information for issuing pattern matching
--- warnings. See Note [Type normalisation for EmptyCase] for details.
---
--- 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_maybe env ty_cs typ
- = do (_, mb_typ') <- liftD $ initTcDsForSolver $ tcNormalise ty_cs 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 for EmptyCase].
- pure $ do typ' <- mb_typ'
- ((ty_f,tm_f), ty) <- topNormaliseTypeX stepper comb typ'
- -- We need to do topNormaliseTypeX in addition to tcNormalise,
- -- since topNormaliseX looks through newtypes, which
- -- tcNormalise does not do.
- Just (eq_src_ty ty (typ' : ty_f [ty]), tm_f [], ty)
- where
- -- Find the first type in the sequence of rewrites that is a data type,
- -- newtype, or a data family application (not the representation tycon!).
- -- This is the one that is equal (in source Haskell) to the initial type.
- -- If none is found in the list, then all of them are type family
- -- applications, so we simply return the last one, which is the *simplest*.
- eq_src_ty :: Type -> [Type] -> Type
- eq_src_ty ty tys = maybe ty id (find is_closed_or_data_family tys)
-
- is_closed_or_data_family :: Type -> Bool
- is_closed_or_data_family ty = pmIsClosedType ty || isDataFamilyAppType ty
-
- -- For efficiency, represent both lists as difference lists.
- -- comb performs the concatenation, for both lists.
- comb (tyf1, tmf1) (tyf2, tmf2) = (tyf1 . tyf2, tmf1 . tmf2)
-
- stepper = newTypeStepper `composeSteppers` tyFamStepper
-
- -- A 'NormaliseStepper' that unwraps newtypes, careful not to fall into
- -- a loop. If it would fall into a loop, it produces 'NS_Abort'.
- newTypeStepper :: NormaliseStepper ([Type] -> [Type],[DataCon] -> [DataCon])
- newTypeStepper rec_nts tc tys
- | Just (ty', _co) <- instNewTyCon_maybe tc tys
- = case checkRecTc rec_nts tc of
- Just rec_nts' -> let tyf = ((TyConApp tc tys):)
- tmf = ((tyConSingleDataCon tc):)
- in NS_Step rec_nts' ty' (tyf, tmf)
- Nothing -> NS_Abort
- | otherwise
- = NS_Done
-
- tyFamStepper :: NormaliseStepper ([Type] -> [Type], [DataCon] -> [DataCon])
- tyFamStepper rec_nts tc tys -- Try to step a type/data family
- = let (_args_co, ntys, _res_co) = normaliseTcArgs env Representational tc tys in
- -- NB: It's OK to use normaliseTcArgs here instead of
- -- normalise_tc_args (which takes the LiftingContext described
- -- in Note [Normalising types]) because the reduceTyFamApp below
- -- works only at top level. We'll never recur in this function
- -- after reducing the kind of a bound tyvar.
-
- case reduceTyFamApp_maybe env Representational tc ntys of
- Just (_co, rhs) -> NS_Step rec_nts rhs ((rhs:), id)
- _ -> NS_Done
-
--- | Determine suitable constraints to use at the beginning of pattern-match
--- coverage checking by consulting the sets of term and type constraints
--- currently in scope. If one of these sets of constraints is unsatisfiable,
--- use an empty set in its place. (See
--- @Note [Recovering from unsatisfiable pattern-matching constraints]@
--- for why this is done.)
-pmInitialTmTyCs :: PmM Delta
-pmInitialTmTyCs = do
- ty_cs <- liftD getDictsDs
- tm_cs <- bagToList <$> liftD getTmCsDs
- sat_ty <- tyOracle ty_cs
- let initTyCs = if sat_ty then ty_cs else emptyBag
- initTmState = fromMaybe initialTmState (tmOracle initialTmState tm_cs)
- pure $ MkDelta{ delta_tm_cs = initTmState, delta_ty_cs = initTyCs }
+ Left ty -> pure (TypeOfUncovered ty)
+ -- A list of oracle states for the different satisfiable constructors is
+ -- available. Turn this into a value set abstraction.
+ Right (va, deltas) -> pure (UncoveredPatterns [va] deltas)
+ pure (PmResult [] us [])
+
+getNFirstUncovered :: [Id] -> Int -> [Delta] -> DsM [Delta]
+getNFirstUncovered _ 0 _ = pure []
+getNFirstUncovered _ _ [] = pure []
+getNFirstUncovered vars n (delta:deltas) = do
+ front <- provideEvidenceForEquation vars n delta
+ back <- getNFirstUncovered vars (n - length front) deltas
+ pure (front ++ back)
+
+-- | The maximum successive number of refinements ('refineToAltCon') we allow
+-- per representative. See Note [Limit the number of refinements].
+mAX_REFINEMENTS :: Int
+-- The current number is chosen so that PrelRules is still checked with
+-- reasonable performance. If this is set to below 2, ds022 will start to fail.
+-- Although that is probably due to the fact that we always increase the
+-- refinement counter instead of just increasing it when the contraposition
+-- is satisfiable (when the not taken case 'addRefutableAltCon' is
+-- satisfiable, that is). That would be the first thing I'd try if we have
+-- performance problems on one test while decreasing the threshold leads to
+-- other tests being broken like ds022 above.
+mAX_REFINEMENTS = 3
+
+-- | The threshold for detecting exponential blow-up in the number of 'Delta's
+-- to check introduced by guards.
+tHRESHOLD_GUARD_DELTAS :: Int
+tHRESHOLD_GUARD_DELTAS = 100
+
+-- | Multiply the estimated number of 'Delta's to process by a constant
+-- branching factor induced by a guard and return the new estimate if it
+-- doesn't exceed a constant threshold.
+-- See 6. in Note [Guards and Approximation].
+tryMultiplyDeltas :: Int -> Int -> Maybe Int
+tryMultiplyDeltas multiplier n_delta
+ -- The ^2 below is intentional: We want to give up on guards with a large
+ -- branching factor rather quickly, still leaving room for small informative
+ -- ones later on.
+ | n_delta*multiplier^(2::Int) < tHRESHOLD_GUARD_DELTAS
+ = Just (n_delta*multiplier)
+ | otherwise
+ = Nothing
{-
-Note [Recovering from unsatisfiable pattern-matching constraints]
-~~~~~~~~~~~~~~~~
-Consider the following code (see #12957 and #15450):
-
- f :: Int ~ Bool => ()
- f = case True of { False -> () }
-
-We want to warn that the pattern-matching in `f` is non-exhaustive. But GHC
-used not to do this; in fact, it would warn that the match was /redundant/!
-This is because the constraint (Int ~ Bool) in `f` is unsatisfiable, and the
-coverage checker deems any matches with unsatifiable constraint sets to be
-unreachable.
-
-We decide to better than this. When beginning coverage checking, we first
-check if the constraints in scope are unsatisfiable, and if so, we start
-afresh with an empty set of constraints. This way, we'll get the warnings
-that we expect.
--}
-
--- | 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.)
---
--- For the purposes of efficiency, this takes as separate arguments the
--- ambient term and type constraints (which are known beforehand to be
--- satisfiable), as well as the new term and type constraints (which may not
--- be satisfiable). This lets us implement two mini-optimizations:
---
--- * If there are no new type constraints, then don't bother initializing
--- the type oracle, since it's redundant to do so.
--- * Since the new term constraint is a separate argument, we only need to
--- execute one iteration of the term oracle (instead of traversing the
--- entire set of term constraints).
---
--- 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 [Extensions to GADTs Meet Their Match]@.
-pmIsSatisfiable
- :: Delta -- ^ The ambient term and type constraints
- -- (known to be satisfiable).
- -> TmVarCt -- ^ The new term constraint.
- -> Bag EvVar -- ^ The new type constraints.
- -> [Type] -- ^ The strict argument types.
- -> PmM (Maybe Delta)
- -- ^ @'Just' delta@ if the constraints (@delta@) are
- -- satisfiable, and each strict argument type is inhabitable.
- -- 'Nothing' otherwise.
-pmIsSatisfiable amb_cs new_tm_c new_ty_cs strict_arg_tys = do
- mb_sat <- tmTyCsAreSatisfiable amb_cs new_tm_c new_ty_cs
- case mb_sat of
- Nothing -> pure Nothing
- Just delta -> do
- -- We know that the term and type constraints are inhabitable, so now
- -- check if each strict argument type is inhabitable.
- all_non_void <- checkAllNonVoid initRecTc delta strict_arg_tys
- pure $ if all_non_void -- Check if each strict argument type
- -- is inhabitable
- then Just delta
- else Nothing
-
--- | Like 'pmIsSatisfiable', but only checks if term and type constraints are
--- satisfiable, and doesn't bother checking anything related to strict argument
--- types.
-tmTyCsAreSatisfiable
- :: Delta -- ^ The ambient term and type constraints
- -- (known to be satisfiable).
- -> TmVarCt -- ^ The new term constraint.
- -> Bag EvVar -- ^ The new type constraints.
- -> PmM (Maybe Delta)
- -- ^ @'Just' delta@ if the constraints (@delta@) are
- -- satisfiable. 'Nothing' otherwise.
-tmTyCsAreSatisfiable
- (MkDelta{ delta_tm_cs = amb_tm_cs, delta_ty_cs = amb_ty_cs })
- new_tm_c new_ty_cs = do
- let ty_cs = new_ty_cs `unionBags` amb_ty_cs
- sat_ty <- if isEmptyBag new_ty_cs
- then pure True
- else tyOracle ty_cs
- pure $ case (sat_ty, solveOneEq amb_tm_cs new_tm_c) of
- (True, Just term_cs) -> Just $ MkDelta{ delta_ty_cs = ty_cs
- , delta_tm_cs = term_cs }
- _unsat -> Nothing
-
--- | Implements two performance optimizations, as described in the
--- \"Strict argument type constraints\" section of
--- @Note [Extensions to GADTs Meet Their Match]@.
-checkAllNonVoid :: RecTcChecker -> Delta -> [Type] -> PmM Bool
-checkAllNonVoid rec_ts amb_cs strict_arg_tys = do
- fam_insts <- liftD dsGetFamInstEnvs
- let definitely_inhabited =
- definitelyInhabitedType fam_insts (delta_ty_cs amb_cs)
- tys_to_check <- filterOutM definitely_inhabited strict_arg_tys
- let rec_max_bound | tys_to_check `lengthExceeds` 1
- = 1
- | otherwise
- = defaultRecTcMaxBound
- 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 [Extensions to GADTs Meet Their Match]@.
-nonVoid
- :: RecTcChecker -- ^ The per-'TyCon' recursion depth limit.
- -> Delta -- ^ The ambient term/type constraints (known to be
- -- satisfiable).
- -> Type -- ^ The strict argument type.
- -> PmM 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 (delta_ty_cs amb_cs) strict_arg_ty
- case mb_cands of
- Right (tc, cands)
- | 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 [Extensions to GADTs Meet Their Match]
- cand_is_inhabitable :: RecTcChecker -> Delta
- -> InhabitationCandidate -> PmM Bool
- cand_is_inhabitable rec_ts amb_cs
- (InhabitationCandidate{ ic_tm_ct = new_term_c
- , ic_ty_cs = new_ty_cs
- , ic_strict_arg_tys = new_strict_arg_tys }) = do
- mb_sat <- tmTyCsAreSatisfiable amb_cs new_term_c new_ty_cs
- case mb_sat of
- Nothing -> pure False
- Just new_delta -> do
- checkAllNonVoid rec_ts new_delta 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 \"Strict argument type constraints\" section of
--- @Note [Extensions to GADTs Meet Their Match]@.
-definitelyInhabitedType :: FamInstEnvs -> Bag EvVar -> Type -> PmM Bool
-definitelyInhabitedType env ty_cs ty = do
- mb_res <- pmTopNormaliseType_maybe env ty_cs ty
- pure $ case mb_res of
- Just (_, cons, _) -> any meets_criteria cons
- Nothing -> False
- where
- meets_criteria :: DataCon -> Bool
- meets_criteria con =
- null (dataConEqSpec con) && -- (1)
- null (dataConImplBangs con) -- (2)
-
-{- Note [Type normalisation for EmptyCase]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-EmptyCase is an exception for pattern matching, since it is strict. This means
-that it boils down to checking whether the type of the scrutinee is inhabited.
-Function pmTopNormaliseType_maybe gets rid of the outermost type function/data
-family redex and newtypes, in search of an algebraic type constructor, which is
-easier to check for inhabitation.
-
-It returns 3 results instead of one, because there are 2 subtle points:
-1. Newtypes are isomorphic to the underlying type in core but not in the source
- language,
-2. The representational data family tycon is used internally but should not be
- shown to the user
-
-Hence, if pmTopNormaliseType_maybe env ty_cs ty = Just (src_ty, dcs, core_ty),
-then
- (a) src_ty is the rewritten type which we can show to the user. That is, the
- type we get if we rewrite type families but not data families or
- newtypes.
- (b) dcs is the list of data constructors "skipped", every time we normalise a
- newtype to its core representation, we keep track of the source data
- constructor.
- (c) core_ty is the rewritten type. That is,
- pmTopNormaliseType_maybe env ty_cs ty = Just (src_ty, dcs, core_ty)
- implies
- topNormaliseType_maybe env ty = Just (co, core_ty)
- for some coercion co.
-
-To see how all cases come into play, consider the following example:
-
- data family T a :: *
- data instance T Int = T1 | T2 Bool
- -- Which gives rise to FC:
- -- data T a
- -- data R:TInt = T1 | T2 Bool
- -- axiom ax_ti : T Int ~R R:TInt
-
- newtype G1 = MkG1 (T Int)
- newtype G2 = MkG2 G1
-
- type instance F Int = F Char
- type instance F Char = G2
-
-In this case pmTopNormaliseType_maybe env ty_cs (F Int) results in
-
- Just (G2, [MkG2,MkG1], R:TInt)
-
-Which means that in source Haskell:
- - G2 is equivalent to F Int (in contrast, G1 isn't).
- - if (x : R:TInt) then (MkG2 (MkG1 x) : F Int).
-
------
--- Wrinkle: Local equalities
------
-
-Given the following type family:
-
- type family F a
- type instance F Int = Void
-
-Should the following program (from #14813) be considered exhaustive?
-
- f :: (i ~ Int) => F i -> a
- f x = case x of {}
-
-You might think "of course, since `x` is obviously of type Void". But the
-idType of `x` is technically F i, not Void, so if we pass F i to
-inhabitationCandidates, we'll mistakenly conclude that `f` is non-exhaustive.
-In order to avoid this pitfall, we need to normalise the type passed to
-pmTopNormaliseType_maybe, using the constraint solver to solve for any local
-equalities (such as i ~ Int) that may be in scope.
--}
-
--- | 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 :: Bag EvVar -> Type
- -> PmM (Either Type (TyCon, [InhabitationCandidate]))
-inhabitationCandidates ty_cs ty = do
- fam_insts <- liftD dsGetFamInstEnvs
- mb_norm_res <- pmTopNormaliseType_maybe fam_insts ty_cs ty
- case mb_norm_res of
- Just (src_ty, dcs, core_ty) -> alts_to_check src_ty core_ty dcs
- Nothing -> alts_to_check ty ty []
- where
- -- All these types are trivially inhabited
- trivially_inhabited = [ charTyCon, doubleTyCon, floatTyCon
- , intTyCon, wordTyCon, word8TyCon ]
-
- -- Note: At the moment we leave all the typing and constraint fields of
- -- PmCon empty, since we know that they are not gonna be used. Is the
- -- right-thing-to-do to actually create them, even if they are never used?
- build_tm :: ValAbs -> [DataCon] -> ValAbs
- build_tm = foldr (\dc e -> PmCon (RealDataCon dc) [] [] [] [e])
-
- -- Inhabitation candidates, using the result of pmTopNormaliseType_maybe
- alts_to_check :: Type -> Type -> [DataCon]
- -> PmM (Either Type (TyCon, [InhabitationCandidate]))
- alts_to_check src_ty core_ty dcs = case splitTyConApp_maybe core_ty of
- Just (tc, tc_args)
- | tc `elem` trivially_inhabited
- -> case dcs of
- [] -> return (Left src_ty)
- (_:_) -> do var <- liftD $ mkPmId core_ty
- let va = build_tm (PmVar var) dcs
- return $ Right (tc, [InhabitationCandidate
- { ic_val_abs = va, ic_tm_ct = mkIdEq var
- , ic_ty_cs = emptyBag, 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.
- -> liftD $ do
- var <- mkPmId core_ty -- it would be wrong to unify x
- alts <- mapM (mkOneConFull var tc_args . RealDataCon) (tyConDataCons tc)
- return $ Right
- (tc, [ alt{ic_val_abs = build_tm (ic_val_abs alt) dcs}
- | alt <- alts ])
- -- For other types conservatively assume that they are inhabited.
- _other -> return (Left src_ty)
-
-{- Note [Checking EmptyCase Expressions]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Empty case expressions are strict on the scrutinee. That is, `case x of {}`
-will force argument `x`. Hence, `checkMatches` 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_maybe (in types/FamInstEnv.hs). 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 for EmptyCase]
- in types/FamInstEnv.hs.
-
-2. Function 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 for normal
- pattern match checking.
-
%************************************************************************
%* *
Transform source syntax to *our* syntax
@@ -920,14 +425,14 @@ we do the following:
-- -----------------------------------------------------------------------
-- * Utilities
-nullaryConPattern :: ConLike -> Pattern
+nullaryConPattern :: ConLike -> PmPat
-- Nullary data constructor and nullary type constructor
nullaryConPattern con =
- PmCon { pm_con_con = con, pm_con_arg_tys = []
- , pm_con_tvs = [], pm_con_dicts = [], pm_con_args = [] }
+ PmCon { pm_con_con = (PmAltConLike con), pm_con_arg_tys = []
+ , pm_con_tvs = [], pm_con_args = [] }
{-# INLINE nullaryConPattern #-}
-truePattern :: Pattern
+truePattern :: PmPat
truePattern = nullaryConPattern (RealDataCon trueDataCon)
{-# INLINE truePattern #-}
@@ -937,35 +442,54 @@ mkCanFailPmPat ty = do
var <- mkPmVar ty
return [var, PmFake]
-vanillaConPattern :: ConLike -> [Type] -> PatVec -> Pattern
+vanillaConPattern :: ConLike -> [Type] -> PatVec -> PmPat
-- ADT constructor pattern => no existentials, no local constraints
vanillaConPattern con arg_tys args =
- PmCon { pm_con_con = con, pm_con_arg_tys = arg_tys
- , pm_con_tvs = [], pm_con_dicts = [], pm_con_args = args }
+ PmCon { pm_con_con = PmAltConLike con, pm_con_arg_tys = arg_tys
+ , pm_con_tvs = [], pm_con_args = args }
{-# INLINE vanillaConPattern #-}
-- | Create an empty list pattern of a given type
-nilPattern :: Type -> Pattern
+nilPattern :: Type -> PmPat
nilPattern ty =
- PmCon { pm_con_con = RealDataCon nilDataCon, pm_con_arg_tys = [ty]
- , pm_con_tvs = [], pm_con_dicts = []
- , pm_con_args = [] }
+ PmCon { pm_con_con = PmAltConLike (RealDataCon nilDataCon)
+ , pm_con_arg_tys = [ty], pm_con_tvs = [], pm_con_args = [] }
{-# INLINE nilPattern #-}
mkListPatVec :: Type -> PatVec -> PatVec -> PatVec
-mkListPatVec ty xs ys = [PmCon { pm_con_con = RealDataCon consDataCon
+mkListPatVec ty xs ys = [PmCon { pm_con_con = PmAltConLike (RealDataCon consDataCon)
, pm_con_arg_tys = [ty]
- , pm_con_tvs = [], pm_con_dicts = []
+ , pm_con_tvs = []
, pm_con_args = xs++ys }]
{-# INLINE mkListPatVec #-}
--- | Create a (non-overloaded) literal pattern
-mkLitPattern :: HsLit GhcTc -> Pattern
-mkLitPattern lit = PmLit { pm_lit_lit = PmSLit lit }
-{-# INLINE mkLitPattern #-}
+-- | Create a literal pattern
+mkPmLitPattern :: PmLit -> PatVec
+mkPmLitPattern lit@(PmLit _ val)
+ -- We translate String literals to list literals for better overlap reasoning.
+ -- It's a little unfortunate we do this here rather than in
+ -- 'PmOracle.trySolve' and 'PmOracle.addRefutableAltCon', but it's so much
+ -- simpler here.
+ -- See Note [Representation of Strings in TmState] in PmOracle
+ | PmLitString s <- val
+ , let mk_char_lit c = mkPmLitPattern (PmLit charTy (PmLitChar c))
+ = foldr (\c p -> mkListPatVec charTy (mk_char_lit c) p)
+ [nilPattern charTy]
+ (unpackFS s)
+ | otherwise
+ = [PmCon { pm_con_con = PmAltLit lit
+ , pm_con_arg_tys = []
+ , pm_con_tvs = []
+ , pm_con_args = [] }]
+{-# INLINE mkPmLitPattern #-}
-- -----------------------------------------------------------------------
--- * Transform (Pat Id) into of (PmPat Id)
+-- * Transform (Pat Id) into [PmPat]
+-- The arity of the [PmPat] is always 1, but it may be a combination
+-- of a vanilla pattern and a guard pattern.
+-- Example: view pattern (f y -> Just x)
+-- becomes [PmVar z, PmGrd [PmPat (Just x), f y]]
+-- where z is fresh
translatePat :: FamInstEnvs -> Pat GhcTc -> DsM PatVec
translatePat fam_insts pat = case pat of
@@ -977,12 +501,10 @@ translatePat fam_insts pat = case pat of
-- ignore strictness annotations for now
BangPat _ p -> translatePat fam_insts (unLoc p)
- AsPat _ lid p -> do
- -- Note [Translating As Patterns]
- ps <- translatePat fam_insts (unLoc p)
- let [e] = map vaToPmExpr (coercePatVec ps)
- g = PmGrd [PmVar (unLoc lid)] e
- return (ps ++ [g])
+ -- (x@pat) ===> x (pat <- x)
+ AsPat _ (dL->L _ x) p -> do
+ pat <- translatePat fam_insts (unLoc p)
+ pure [PmVar x, PmGrd pat (Var x)]
SigPat _ p _ty -> translatePat fam_insts (unLoc p)
@@ -991,10 +513,10 @@ translatePat fam_insts pat = case pat of
| isIdHsWrapper wrapper -> translatePat fam_insts p
| WpCast co <- wrapper, isReflexiveCo co -> translatePat fam_insts p
| otherwise -> do
- ps <- translatePat fam_insts p
+ ps <- translatePat fam_insts p
(xp,xe) <- mkPmId2Forms ty
g <- mkGuard ps (mkHsWrap wrapper (unLoc xe))
- return [xp,g]
+ pure [xp,g]
-- (n + k) ===> x (True <- x >= k) (n <- x-k)
NPlusKPat ty (dL->L _ _n) _k1 _k2 _ge _minus -> mkCanFailPmPat ty
@@ -1008,20 +530,20 @@ translatePat fam_insts pat = case pat of
True -> do
(xp,xe) <- mkPmId2Forms arg_ty
g <- mkGuard ps (HsApp noExtField lexpr xe)
- return [xp,g]
+ return [xp, g]
False -> mkCanFailPmPat arg_ty
-- list
ListPat (ListPatTc ty Nothing) ps -> do
- foldr (mkListPatVec ty) [nilPattern ty]
- <$> translatePatVec fam_insts (map unLoc ps)
+ pv <- translatePatVec fam_insts (map unLoc ps)
+ return (foldr (mkListPatVec ty) [nilPattern ty] pv)
-- overloaded list
ListPat (ListPatTc _elem_ty (Just (pat_ty, _to_list))) lpats -> do
dflags <- getDynFlags
if xopt LangExt.RebindableSyntax dflags
- then mkCanFailPmPat pat_ty
- else case splitListTyConApp_maybe pat_ty of
+ then mkCanFailPmPat pat_ty
+ else case splitListTyConApp_maybe pat_ty of
Just e_ty -> translatePat fam_insts
(ListPat (ListPatTc e_ty Nothing) lpats)
Nothing -> mkCanFailPmPat pat_ty
@@ -1046,43 +568,50 @@ translatePat fam_insts pat = case pat of
ConPatOut { pat_con = (dL->L _ con)
, pat_arg_tys = arg_tys
, pat_tvs = ex_tvs
- , pat_dicts = dicts
, pat_args = ps } -> do
- groups <- allCompleteMatches con arg_tys
+ let ty = conLikeResTy con arg_tys
+ groups <- allCompleteMatches ty
case groups of
- [] -> mkCanFailPmPat (conLikeResTy con arg_tys)
+ [] -> mkCanFailPmPat ty
_ -> do
args <- translateConPatVec fam_insts arg_tys ex_tvs con ps
- return [PmCon { pm_con_con = con
+ return [PmCon { pm_con_con = PmAltConLike con
, pm_con_arg_tys = arg_tys
, pm_con_tvs = ex_tvs
- , pm_con_dicts = dicts
, pm_con_args = args }]
- -- See Note [Translate Overloaded Literal for Exhaustiveness Checking]
- NPat _ (dL->L _ olit) mb_neg _
- | OverLit (OverLitTc False ty) (HsIsString src s) _ <- olit
- , isStringTy ty ->
- foldr (mkListPatVec charTy) [nilPattern charTy] <$>
- translatePatVec fam_insts
- (map (LitPat noExtField . HsChar src) (unpackFS s))
- | otherwise -> return [PmLit { pm_lit_lit = PmOLit (isJust mb_neg) olit }]
-
- -- See Note [Translate Overloaded Literal for Exhaustiveness Checking]
- LitPat _ lit
- | HsString src s <- lit ->
- foldr (mkListPatVec charTy) [nilPattern charTy] <$>
- translatePatVec fam_insts
- (map (LitPat noExtField . HsChar src) (unpackFS s))
- | otherwise -> return [mkLitPattern lit]
+ NPat ty (dL->L _ olit) mb_neg _ -> do
+ -- See Note [Literal short cut] in MatchLit.hs
+ -- We inline the Literal short cut for @ty@ here, because @ty@ is more
+ -- precise than the field of OverLitTc, which is all that dsOverLit (which
+ -- normally does the literal short cut) can look at. Also @ty@ matches the
+ -- type of the scrutinee, so info on both pattern and scrutinee (for which
+ -- short cutting in dsOverLit works properly) is overloaded iff either is.
+ dflags <- getDynFlags
+ core_expr <- case olit of
+ OverLit{ ol_val = val, ol_ext = OverLitTc rebindable _ }
+ | not rebindable
+ , Just expr <- shortCutLit dflags val ty
+ -> dsExpr expr
+ _ -> dsOverLit olit
+ let lit = expectJust "failed to detect OverLit" (coreExprAsPmLit core_expr)
+ let lit' = case mb_neg of
+ Just _ -> expectJust "failed to negate lit" (negatePmLit lit)
+ Nothing -> lit
+ return (mkPmLitPattern lit')
+
+ LitPat _ lit -> do
+ core_expr <- dsLit (convertLit lit)
+ let lit = expectJust "failed to detect Lit" (coreExprAsPmLit core_expr)
+ return (mkPmLitPattern lit)
TuplePat tys ps boxity -> do
tidy_ps <- translatePatVec fam_insts (map unLoc ps)
let tuple_con = RealDataCon (tupleDataCon boxity (length ps))
tys' = case boxity of
- Boxed -> tys
- -- See Note [Unboxed tuple RuntimeRep vars] in TyCon
- Unboxed -> map getRuntimeRep tys ++ tys
+ Boxed -> tys
+ -- See Note [Unboxed tuple RuntimeRep vars] in TyCon
+ Unboxed -> map getRuntimeRep tys ++ tys
return [vanillaConPattern tuple_con tys' (concat tidy_ps)]
SumPat ty p alt arity -> do
@@ -1097,91 +626,6 @@ translatePat fam_insts pat = case pat of
SplicePat {} -> panic "Check.translatePat: SplicePat"
XPat {} -> panic "Check.translatePat: XPat"
-{- Note [Translate Overloaded Literal for Exhaustiveness Checking]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The translation of @NPat@ in exhaustiveness checker is a bit different
-from translation in pattern matcher.
-
- * In pattern matcher (see `tidyNPat' in deSugar/MatchLit.hs), we
- translate integral literals to HsIntPrim or HsWordPrim and translate
- overloaded strings to HsString.
-
- * In exhaustiveness checker, in `genCaseTmCs1/genCaseTmCs2`, we use
- `lhsExprToPmExpr` to generate uncovered set. In `hsExprToPmExpr`,
- however we generate `PmOLit` for HsOverLit, rather than refine
- `HsOverLit` inside `NPat` to HsIntPrim/HsWordPrim. If we do
- the same thing in `translatePat` as in `tidyNPat`, the exhaustiveness
- checker will fail to match the literals patterns correctly. See
- #14546.
-
- In Note [Undecidable Equality for Overloaded Literals], we say: "treat
- overloaded literals that look different as different", but previously we
- didn't do such things.
-
- Now, we translate the literal value to match and the literal patterns
- consistently:
-
- * For integral literals, we parse both the integral literal value and
- the patterns as OverLit HsIntegral. For example:
-
- case 0::Int of
- 0 -> putStrLn "A"
- 1 -> putStrLn "B"
- _ -> putStrLn "C"
-
- When checking the exhaustiveness of pattern matching, we translate the 0
- in value position as PmOLit, but translate the 0 and 1 in pattern position
- as PmSLit. The inconsistency leads to the failure of eqPmLit to detect the
- equality and report warning of "Pattern match is redundant" on pattern 0,
- as reported in #14546. In this patch we remove the specialization of
- OverLit patterns, and keep the overloaded number literal in pattern as it
- is to maintain the consistency. We know nothing about the `fromInteger`
- method (see Note [Undecidable Equality for Overloaded Literals]). Now we
- can capture the exhaustiveness of pattern 0 and the redundancy of pattern
- 1 and _.
-
- * For string literals, we parse the string literals as HsString. When
- OverloadedStrings is enabled, it further be turned as HsOverLit HsIsString.
- For example:
-
- case "foo" of
- "foo" -> putStrLn "A"
- "bar" -> putStrLn "B"
- "baz" -> putStrLn "C"
-
- Previously, the overloaded string values are translated to PmOLit and the
- non-overloaded string values are translated to PmSLit. However the string
- patterns, both overloaded and non-overloaded, are translated to list of
- characters. The inconsistency leads to wrong warnings about redundant and
- non-exhaustive pattern matching warnings, as reported in #14546.
-
- In order to catch the redundant pattern in following case:
-
- case "foo" of
- ('f':_) -> putStrLn "A"
- "bar" -> putStrLn "B"
-
- in this patch, we translate non-overloaded string literals, both in value
- position and pattern position, as list of characters. For overloaded string
- literals, we only translate it to list of characters only when it's type
- is stringTy, since we know nothing about the toString methods. But we know
- that if two overloaded strings are syntax equal, then they are equal. Then
- if it's type is not stringTy, we just translate it to PmOLit. We can still
- capture the exhaustiveness of pattern "foo" and the redundancy of pattern
- "bar" and "baz" in the following code:
-
- {-# LANGUAGE OverloadedStrings #-}
- main = do
- case "foo" of
- "foo" -> putStrLn "A"
- "bar" -> putStrLn "B"
- "baz" -> putStrLn "C"
-
- We must ensure that doing the same translation to literal values and patterns
- in `translatePat` and `hsExprToPmExpr`. The previous inconsistent work led to
- #14546.
--}
-
-- | Translate a list of patterns (Note: each pattern is translated
-- to a pattern vector but we do not concatenate the results).
translatePatVec :: FamInstEnvs -> [Pat GhcTc] -> DsM [PatVec]
@@ -1189,7 +633,8 @@ translatePatVec fam_insts pats = mapM (translatePat fam_insts) pats
-- | Translate a constructor pattern
translateConPatVec :: FamInstEnvs -> [Type] -> [TyVar]
- -> ConLike -> HsConPatDetails GhcTc -> DsM PatVec
+ -> ConLike -> HsConPatDetails GhcTc
+ -> DsM PatVec
translateConPatVec fam_insts _univ_tys _ex_tvs _ (PrefixCon ps)
= concat <$> translatePatVec fam_insts (map unLoc ps)
translateConPatVec fam_insts _univ_tys _ex_tvs _ (InfixCon p1 p2)
@@ -1221,7 +666,7 @@ translateConPatVec fam_insts univ_tys ex_tvs c (RecCon (HsRecFields fs _))
let zipped = zip orig_lbls [ x | PmVar x <- arg_var_pats ]
guards = map (\(name,pvec) -> case lookup name zipped of
- Just x -> PmGrd pvec (PmExprVar (idName x))
+ Just x -> PmGrd pvec (Var x)
Nothing -> panic "translateConPatVec: lookup")
translated_pats
@@ -1245,19 +690,20 @@ translateConPatVec fam_insts univ_tys ex_tvs c (RecCon (HsRecFields fs _))
-- Translate a single match
translateMatch :: FamInstEnvs -> LMatch GhcTc (LHsExpr GhcTc)
- -> DsM (PatVec,[PatVec])
-translateMatch fam_insts (dL->L _ (Match { m_pats = lpats, m_grhss = grhss })) =
- do
- pats' <- concat <$> translatePatVec fam_insts pats
- guards' <- mapM (translateGuards fam_insts) guards
- return (pats', guards')
- where
- extractGuards :: LGRHS GhcTc (LHsExpr GhcTc) -> [GuardStmt GhcTc]
- extractGuards (dL->L _ (GRHS _ gs _)) = map unLoc gs
- extractGuards _ = panic "translateMatch"
-
- pats = map unLoc lpats
- guards = map extractGuards (grhssGRHSs grhss)
+ -> DsM (PatVec, [PatVec])
+translateMatch fam_insts (dL->L _ (Match { m_pats = lpats, m_grhss = grhss }))
+ = do
+ pats' <- concat <$> translatePatVec fam_insts pats
+ guards' <- mapM (translateGuards fam_insts) guards
+ -- tracePm "translateMatch" (vcat [ppr pats, ppr pats', ppr guards, ppr guards'])
+ return (pats', guards')
+ where
+ extractGuards :: LGRHS GhcTc (LHsExpr GhcTc) -> [GuardStmt GhcTc]
+ extractGuards (dL->L _ (GRHS _ gs _)) = map unLoc gs
+ extractGuards _ = panic "translateMatch"
+
+ pats = map unLoc lpats
+ guards = map extractGuards (grhssGRHSs grhss)
translateMatch _ _ = panic "translateMatch"
-- -----------------------------------------------------------------------
@@ -1265,35 +711,11 @@ translateMatch _ _ = panic "translateMatch"
-- | Translate a list of guard statements to a pattern vector
translateGuards :: FamInstEnvs -> [GuardStmt GhcTc] -> DsM PatVec
-translateGuards fam_insts guards = do
- all_guards <- concat <$> mapM (translateGuard fam_insts) guards
- let
- shouldKeep :: Pattern -> DsM Bool
- shouldKeep p
- | PmVar {} <- p = pure True
- | PmCon {} <- p = (&&)
- <$> singleMatchConstructor (pm_con_con p) (pm_con_arg_tys p)
- <*> allM shouldKeep (pm_con_args p)
- shouldKeep (PmGrd pv e)
- | isNotPmExprOther e = pure True -- expensive but we want it
- | otherwise = allM shouldKeep pv
- shouldKeep _other_pat = pure False -- let the rest..
-
- all_handled <- allM shouldKeep all_guards
- -- It should have been @pure all_guards@ but it is too expressive.
- -- Since the term oracle does not handle all constraints we generate,
- -- we (hackily) replace all constraints the oracle cannot handle with a
- -- single one (we need to know if there is a possibility of failure).
- -- See Note [Guards and Approximation] for all guard-related approximations
- -- we implement.
- if all_handled
- then pure all_guards
- else do
- kept <- filterM shouldKeep all_guards
- pure (PmFake : kept)
+translateGuards fam_insts guards =
+ concat <$> mapM (translateGuard fam_insts) guards
-- | Check whether a pattern can fail to match
-cantFailPattern :: Pattern -> DsM Bool
+cantFailPattern :: PmPat -> DsM Bool
cantFailPattern PmVar {} = pure True
cantFailPattern PmCon { pm_con_con = c, pm_con_arg_tys = tys, pm_con_args = ps}
= (&&) <$> singleMatchConstructor c tys <*> allM cantFailPattern ps
@@ -1395,11 +817,10 @@ below is the *right thing to do*:
The case with literals is a bit different. a literal @l@ should be translated
to @x (True <- x == from l)@. Since we want to have better warnings for
overloaded literals as it is a very common feature, we treat them differently.
-They are mainly covered in Note [Undecidable Equality for Overloaded Literals]
-in PmExpr.
+They are mainly covered in Note [Undecidable Equality for PmAltCons] in PmExpr.
4. N+K Patterns & Pattern Synonyms
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+----------------------------------
An n+k pattern (n+k) should be translated to @x (True <- x >= k) (n <- x-k)@.
Since the only pattern of the three that causes failure is guard @(n <- x-k)@,
and has two possible outcomes. Hence, there is no benefit in using a dummy and
@@ -1418,18 +839,85 @@ Additionally, top-level guard translation (performed by @translateGuards@)
replaces guards that cannot be reasoned about (like the ones we described in
1-4) with a single @PmFake@ to record the possibility of failure to match.
+6. Combinatorial explosion
+--------------------------
+Function with many clauses and deeply nested guards like in #11195 tend to
+overwhelm the checker because they lead to exponential splitting behavior.
+See the comments on #11195 on refinement trees. Every guard refines the
+disjunction of Deltas by another split. This no different than the ConVar case,
+but in stark contrast we mostly don't get any useful information out of that
+split! Hence splitting k-fold just means having k-fold more work. The problem
+exacerbates for larger k, because it gets even more unlikely that we can handle
+all of the arising Deltas better than just continue working on the original
+Delta.
+Long story short: At each point we estimate the number of Deltas we possibly
+have to check in the same manner as the current Delta. If we hit a guard that
+splits the current Delta k-fold, we check whether this split would get us beyond
+a certain threshold ('tryMultiplyDeltas') and continue to check the other guards
+with the original Delta.
+
+Note [Limit the number of refinements]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In PrelRules, we have a huge case with relatively deep matches on pattern
+synonyms. Since we allow multiple compatible solutions in the oracle
+(i.e. @x ~ [I# y, 42]@), and every pattern synonym is compatible according to
+'eqPmAltCon' with every other (no generativity as with DataCons), what would
+usually result in a ConVar split where only one branch is satisfiable results
+in a blow-up of Deltas. Here's an example:
+ case x of
+ A (A _) -> ()
+ B (B _) -> ()
+ ...
+By the time we hit the first clause's RHS, we have split the initial Delta twice
+and handled the {x~A y, y ~ A z} case, leaving {x/~A} and {x~A y, y/~A} models
+for the second clause to check.
+
+Now consider what happens if A=Left, B=Right. We get x~B y' from the match,
+which contradicts with {x~A y, y/~A}, because A and B are incompatible due to
+the generative nature of DataCons. This leaves only {x/~A} for checking B's
+clause, which ultimately leaves {x/~[A,B]} and {x~B y', y'/~B} uncovered.
+Resulting in three models to check for the next clause. That's only linear
+growth in the number of models for each clause.
+
+Consider A and B were arbitrary pattern synonyms instead. We still get x~B y'
+from the match, but this no longer refutes {x~A y, y/~A}, because we don't
+assume generativity for pattern synonyms. Ergo, @eqPmAltCon A B == Nothing@
+and we get to check the second clause's inner match with {x~B y', x/~A} and
+{x~[A y,B y'], y/~A}, splitting both in turn. That makes 4 instead of 3 deltas.
+If we keep on doing this, we see that in the nth clause we'd have O(2^n) models
+to check instead of just O(n) as above!
+
+Clearly we have to put a stop to this. So we count in the oracle the number of
+times we refined x to some constructor. If the number of splits exceeds the
+'mAX_REFINEMENTS', we check the next clause using the original Delta rather
+than the union of Deltas arising from the ConVar split.
+
+If for the above example we had mAX_REFINEMENTS=1, then in the second clause
+we would still check the inner match with {x~B y', x/~A} and {x~[A y,B y'], y/~A}
+but *discard* the two Deltas arising from splitting {x~[A y,B y'], y/~A},
+checking the next clause with {x~A y, y/~A} instead of its two refinements.
+In addition to {x~B y', y'~B z', x/~A} (which arose from the other split) and
+{x/~[A,B]} that makes 3 models for the third equation, so linear :).
+
Note [Translate CoPats]
~~~~~~~~~~~~~~~~~~~~~~~
The pattern match checker did not know how to handle coerced patterns `CoPat`
efficiently, which gave rise to #11276. The original approach translated
`CoPat`s:
- pat |> co ===> x (pat <- (e |> co))
+ pat |> co ===> x (pat <- (x |> co))
-Instead, we now check whether the coercion is a hole or if it is just refl, in
-which case we can drop it. Unfortunately, data families generate useful
-coercions so guards are still generated in these cases and checking data
-families is not really efficient.
+Why did we do this seemingly unnecessary expansion in the first place?
+The reason is that the type of @pat |> co@ (which is the type of the value
+abstraction we match against) might be different than that of @pat@. Data
+instances such as @Sing (a :: Bool)@ are a good example of this: If we would
+just drop the coercion, we'd get a type error when matching @pat@ against its
+value abstraction, with the result being that pmIsSatisfiable decides that every
+possible data constructor fitting @pat@ is rejected as uninhabitated, leading to
+a lot of false warnings.
+
+But we can check whether the coercion is a hole or if it is just refl, in
+which case we can drop it.
%************************************************************************
%* *
@@ -1443,31 +931,15 @@ families is not really efficient.
-- | Get the type out of a PmPat. For guard patterns (ps <- e) we use the type
-- of the first (or the single -WHEREVER IT IS- valid to use?) pattern
-pmPatType :: PmPat p -> Type
+pmPatType :: PmPat -> Type
pmPatType (PmCon { pm_con_con = con, pm_con_arg_tys = tys })
- = conLikeResTy con tys
+ = pmAltConType con tys
pmPatType (PmVar { pm_var_id = x }) = idType x
-pmPatType (PmLit { pm_lit_lit = l }) = pmLitType l
-pmPatType (PmNLit { pm_lit_id = x }) = idType x
pmPatType (PmGrd { pm_grd_pv = pv })
= ASSERT(patVecArity pv == 1) (pmPatType p)
where Just p = find ((==1) . patternArity) pv
pmPatType PmFake = pmPatType truePattern
--- | 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 constraint ('ic_tm_ct')
--- and type constraints ('ic_ty_cs') are permitting, and if all of its strict
--- argument types ('ic_strict_arg_tys') are inhabitable.
--- See @Note [Extensions to GADTs Meet Their Match]@.
-data InhabitationCandidate =
- InhabitationCandidate
- { ic_val_abs :: ValAbs
- , ic_tm_ct :: TmVarCt
- , ic_ty_cs :: Bag EvVar
- , ic_strict_arg_tys :: [Type]
- }
-
{-
Note [Extensions to GADTs Meet Their Match]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1476,275 +948,45 @@ checker adheres to. Since the paper's publication, there have been some
additional features added to the coverage checker which are not described in
the paper. This Note serves as a reference for these new features.
------
--- 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.
-
-`nonVoid ty` returns True when either:
-1. `ty` has at least one InhabitationCandidate for which both its term and type
- constraints are satifiable, 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.
-
-* Performance considerations
-
-We must be careful when recursively calling `nonVoid` on the strict argument
-types of an InhabitationCandidate, because doing so naïvely can cause GHC to
-fall into an infinite loop. 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.
-
-Another microoptimization applies to data types like this one:
-
- data S a = ![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 cheap 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.
+* Value abstractions are severely simplified to the point where they are just
+ variables. The information about the PmExpr shape of a variable is encoded in
+ the oracle state 'Delta' instead.
+* Handling of uninhabited fields like `!Void`.
+ See Note [Strict argument type constraints] in PmOracle.
+* Efficient handling of literal splitting, large enumerations and accurate
+ redundancy warnings for `COMPLETE` groups through the oracle.
-}
-instance Outputable InhabitationCandidate where
- ppr (InhabitationCandidate { ic_val_abs = va, ic_tm_ct = tm_ct
- , ic_ty_cs = ty_cs
- , ic_strict_arg_tys = strict_arg_tys }) =
- text "InhabitationCandidate" <+>
- vcat [ text "ic_val_abs =" <+> ppr va
- , text "ic_tm_ct =" <+> ppr tm_ct
- , text "ic_ty_cs =" <+> ppr ty_cs
- , text "ic_strict_arg_tys =" <+> ppr strict_arg_tys ]
-
--- | Generate an 'InhabitationCandidate' for a given conlike (generate
--- fresh variables of the appropriate type for arguments)
-mkOneConFull :: Id -> [Type] -> ConLike -> DsM InhabitationCandidate
--- * 'con' K is a conlike of algebraic data type 'T tys'
-
--- * 'tc_args' are the type arguments of the 'con's TyCon T
---
--- * 'x' is the variable for which we encode an equality constraint
--- in the term oracle
---
--- After instantiating the universal tyvars of K to tc_args we get
--- K @tys :: forall bs. Q => s1 .. sn -> T tys
---
--- Suppose y1 is a strict field. Then we get
--- Results: ic_val_abs: K (y1::s1) .. (yn::sn)
--- ic_tm_ct: x ~ K y1..yn
--- ic_ty_cs: Q
--- ic_strict_arg_tys: [s1]
-mkOneConFull x tc_args con = do
- let (univ_tvs, ex_tvs, eq_spec, thetas, _req_theta , arg_tys, _con_res_ty)
- = conLikeFullSig con
- arg_is_banged = map isBanged $ conLikeImplBangs con
- subst1 = zipTvSubst univ_tvs tc_args
-
- (subst, ex_tvs') <- cloneTyVarBndrs subst1 ex_tvs <$> getUniqueSupplyM
-
- -- Field types
- let arg_tys' = substTys subst arg_tys
- -- Fresh term variables (VAs) as arguments to the constructor
- arguments <- mapM mkPmVar arg_tys'
- -- All constraints bound by the constructor (alpha-renamed)
- let theta_cs = substTheta subst (eqSpecPreds eq_spec ++ thetas)
- evvars <- mapM (nameType "pm") theta_cs
- let con_abs = PmCon { pm_con_con = con
- , pm_con_arg_tys = tc_args
- , pm_con_tvs = ex_tvs'
- , pm_con_dicts = evvars
- , pm_con_args = arguments }
- strict_arg_tys = filterByList arg_is_banged arg_tys'
- return $ InhabitationCandidate
- { ic_val_abs = con_abs
- , ic_tm_ct = TVC x (vaToPmExpr con_abs)
- , ic_ty_cs = listToBag evvars
- , ic_strict_arg_tys = strict_arg_tys
- }
-
-- ----------------------------------------------------------------------------
-- * More smart constructors and fresh variable generation
-- | Create a guard pattern
-mkGuard :: PatVec -> HsExpr GhcTc -> DsM Pattern
-mkGuard pv e = do
- res <- allM cantFailPattern pv
- let expr = hsExprToPmExpr e
- tracePmD "mkGuard" (vcat [ppr pv, ppr e, ppr res, ppr expr])
- if | res -> pure (PmGrd pv expr)
- | PmExprOther {} <- expr -> pure PmFake
- | otherwise -> pure (PmGrd pv expr)
-
--- | Create a term equality of the form: `(x ~ lit)`
-mkPosEq :: Id -> PmLit -> TmVarCt
-mkPosEq x l = TVC x (PmExprLit l)
-{-# INLINE mkPosEq #-}
-
--- | Create a term equality of the form: `(x ~ x)`
--- (always discharged by the term oracle)
-mkIdEq :: Id -> TmVarCt
-mkIdEq x = TVC x (PmExprVar (idName x))
-{-# INLINE mkIdEq #-}
+mkGuard :: PatVec -> HsExpr GhcTc -> DsM PmPat
+mkGuard pv e = PmGrd pv <$> dsExpr e
-- | Generate a variable pattern of a given type
-mkPmVar :: Type -> DsM (PmPat p)
+mkPmVar :: Type -> DsM PmPat
mkPmVar ty = PmVar <$> mkPmId ty
-{-# INLINE mkPmVar #-}
-- | Generate many variable patterns, given a list of types
mkPmVars :: [Type] -> DsM PatVec
mkPmVars tys = mapM mkPmVar tys
-{-# INLINE mkPmVars #-}
-
--- | Generate a fresh `Id` of a given type
-mkPmId :: Type -> DsM Id
-mkPmId ty = getUniqueM >>= \unique ->
- let occname = mkVarOccFS $ fsLit "$pm"
- name = mkInternalName unique occname noSrcSpan
- in return (mkLocalId name ty)
-- | Generate a fresh term variable of a given and return it in two forms:
-- * A variable pattern
-- * A variable expression
-mkPmId2Forms :: Type -> DsM (Pattern, LHsExpr GhcTc)
+mkPmId2Forms :: Type -> DsM (PmPat, LHsExpr GhcTc)
mkPmId2Forms ty = do
x <- mkPmId ty
return (PmVar x, noLoc (HsVar noExtField (noLoc x)))
--- ----------------------------------------------------------------------------
--- * Converting between Value Abstractions, Patterns and PmExpr
-
--- | Convert a value abstraction an expression
-vaToPmExpr :: ValAbs -> PmExpr
-vaToPmExpr (PmCon { pm_con_con = c, pm_con_args = ps })
- = PmExprCon c (map vaToPmExpr ps)
-vaToPmExpr (PmVar { pm_var_id = x }) = PmExprVar (idName x)
-vaToPmExpr (PmLit { pm_lit_lit = l }) = PmExprLit l
-vaToPmExpr (PmNLit { pm_lit_id = x }) = PmExprVar (idName x)
-
--- | Convert a pattern vector to a list of value abstractions by dropping the
--- guards (See Note [Translating As Patterns])
-coercePatVec :: PatVec -> [ValAbs]
-coercePatVec pv = concatMap coercePmPat pv
-
--- | Convert a pattern to a list of value abstractions (will be either an empty
--- list if the pattern is a guard pattern, or a singleton list in all other
--- cases) by dropping the guards (See Note [Translating As Patterns])
-coercePmPat :: Pattern -> [ValAbs]
-coercePmPat (PmVar { pm_var_id = x }) = [PmVar { pm_var_id = x }]
-coercePmPat (PmLit { pm_lit_lit = l }) = [PmLit { pm_lit_lit = l }]
-coercePmPat (PmCon { pm_con_con = con, pm_con_arg_tys = arg_tys
- , pm_con_tvs = tvs, pm_con_dicts = dicts
- , pm_con_args = args })
- = [PmCon { pm_con_con = con, pm_con_arg_tys = arg_tys
- , pm_con_tvs = tvs, pm_con_dicts = dicts
- , pm_con_args = coercePatVec args }]
-coercePmPat (PmGrd {}) = [] -- drop the guards
-coercePmPat PmFake = [] -- drop the guards
-
--- | Check whether a 'ConLike' has the /single match/ property, i.e. whether
+-- | Check whether a 'PmAltCon' has the /single match/ property, i.e. whether
-- it is the only possible match in the given context. See also
-- 'allCompleteMatches' and Note [Single match constructors].
-singleMatchConstructor :: ConLike -> [Type] -> DsM Bool
-singleMatchConstructor cl tys =
- any (isSingleton . snd) <$> allCompleteMatches cl tys
+singleMatchConstructor :: PmAltCon -> [Type] -> DsM Bool
+singleMatchConstructor PmAltLit{} _ = pure False
+singleMatchConstructor (PmAltConLike cl) tys =
+ any isSingleton <$> allCompleteMatches (conLikeResTy cl tys)
{-
Note [Single match constructors]
@@ -1771,42 +1013,46 @@ the single-match property. We currently don't (can't) check this in the
translation step. See #15753 for why this yields surprising results.
-}
--- | For a given conlike, finds all the sets of patterns which could
--- be relevant to that conlike by consulting the result type.
+-- | For a given type, finds all the COMPLETE sets of conlikes that inhabit it.
+--
+-- Note that for a data family instance, this must be the *representation* type.
+-- e.g. data instance T (a,b) = T1 a b
+-- leads to
+-- data TPair a b = T1 a b -- The "representation" type
+-- It is TPair a b, not T (a, b), that is given to allCompleteMatches
--
-- These come from two places.
-- 1. From data constructors defined with the result type constructor.
-- 2. From `COMPLETE` pragmas which have the same type as the result
-- type constructor. Note that we only use `COMPLETE` pragmas
-- *all* of whose pattern types match. See #14135
-allCompleteMatches :: ConLike -> [Type] -> DsM [(Provenance, [ConLike])]
-allCompleteMatches cl tys = do
- let fam = case cl of
- RealDataCon dc ->
- [(FromBuiltin, map RealDataCon (tyConDataCons (dataConTyCon dc)))]
- PatSynCon _ -> []
- ty = conLikeResTy cl tys
- pragmas <- case splitTyConApp_maybe ty of
- Just (tc, _) -> dsGetCompleteMatches tc
- Nothing -> return []
- let fams cm = (FromComplete,) <$>
- mapM dsLookupConLike (completeMatchConLikes cm)
- from_pragma <- filter (\(_,m) -> isValidCompleteMatch ty m) <$>
- mapM fams pragmas
- let final_groups = fam ++ from_pragma
- return final_groups
- where
- -- Check that all the pattern synonym return types in a `COMPLETE`
- -- pragma subsume the type we're matching.
- -- See Note [Filtering out non-matching COMPLETE sets]
- isValidCompleteMatch :: Type -> [ConLike] -> Bool
- isValidCompleteMatch ty = all go
- where
- go (RealDataCon {}) = True
- go (PatSynCon psc) = isJust $ flip tcMatchTy ty $ patSynResTy
- $ patSynSig psc
-
- patSynResTy (_, _, _, _, _, res_ty) = res_ty
+allCompleteMatches :: Type -> DsM [[ConLike]]
+allCompleteMatches ty = case splitTyConApp_maybe ty of
+ Nothing -> pure [] -- NB: We don't know any COMPLETE set, as opposed to [[]]
+ Just (tc, tc_args) -> do
+ -- Look into the representation type of a data family instance, too.
+ env <- dsGetFamInstEnvs
+ let (tc', _tc_args', _co) = tcLookupDataFamInst env tc tc_args
+ let mb_rdcs = map RealDataCon <$> tyConDataCons_maybe tc'
+ let maybe_to_list = maybe [] (:[])
+ let rdcs = maybe_to_list mb_rdcs
+ -- NB: tc, because COMPLETE sets are associated with the parent data family
+ -- TyCon
+ pragmas <- dsGetCompleteMatches tc
+ let fams = mapM dsLookupConLike . completeMatchConLikes
+ pscs <- mapM fams pragmas
+ let candidates = rdcs ++ pscs
+ -- Check that all the pattern synonym return types in a `COMPLETE`
+ -- pragma subsume the type we're matching.
+ -- See Note [Filtering out non-matching COMPLETE sets]
+ pure (filter (isValidCompleteMatch ty) candidates)
+ where
+ isValidCompleteMatch :: Type -> [ConLike] -> Bool
+ isValidCompleteMatch ty = all p
+ where
+ p (RealDataCon _) = True
+ p (PatSynCon ps) = isJust (tcMatchTy (projResTy (patSynSig ps)) ty)
+ projResTy (_, _, _, _, _, res_ty) = res_ty
{-
Note [Filtering out non-matching COMPLETE sets]
@@ -1858,37 +1104,24 @@ OK. Consider this example (from #14059):
In the incomplete pattern match for `wobble`, we /do/ want to warn that SFalse
should be matched against, even though its type, SBool False, does not match
the scrutinee type, SBool z.
--}
--- -----------------------------------------------------------------------
--- * Types and constraints
+SG: Another angle at this is that the implied constraints when we instantiate
+universal type variables in the return type of a GADT will lead to *provided*
+thetas, whereas when we instantiate the return type of a pattern synonym that
+corresponds to a *required* theta. See Note [Pattern synonym result type] in
+PatSyn. Note how isValidCompleteMatches will successfully filter out
-newEvVar :: Name -> Type -> EvVar
-newEvVar name ty = mkLocalId name ty
+ pattern Just42 :: Maybe Int
+ pattern Just42 = Just 42
-nameType :: String -> Type -> DsM EvVar
-nameType name ty = do
- unique <- getUniqueM
- let occname = mkVarOccFS (fsLit (name++"_"++show unique))
- idname = mkInternalName unique occname noSrcSpan
- return (newEvVar idname ty)
+But fail to filter out the equivalent
-{-
-%************************************************************************
-%* *
- The type oracle
-%* *
-%************************************************************************
--}
+ pattern Just'42 :: (a ~ Int) => Maybe a
+ pattern Just'42 = Just 42
--- | Check whether a set of type constraints is satisfiable.
-tyOracle :: Bag EvVar -> PmM Bool
-tyOracle evs
- = liftD $
- do { ((_warns, errs), res) <- initTcDsForSolver $ tcCheckSatisfiability evs
- ; case res of
- Just sat -> return sat
- Nothing -> pprPanic "tyOracle" (vcat $ pprErrMsgBagWithLoc errs) }
+Which seems fine as far as tcMatchTy is concerned, but it raises a few eye
+brows.
+-}
{-
%************************************************************************
@@ -1907,8 +1140,9 @@ patVecArity :: PatVec -> PmArity
patVecArity = sum . map patternArity
-- | Compute the arity of a pattern
-patternArity :: Pattern -> PmArity
+patternArity :: PmPat -> PmArity
patternArity (PmGrd {}) = 0
+patternArity PmFake = 0
patternArity _other_pat = 1
{-
@@ -1920,416 +1154,152 @@ patternArity _other_pat = 1
Main functions are:
-* mkInitialUncovered :: [Id] -> PmM Uncovered
-
- Generates the initial uncovered set. Term and type constraints in scope
- are checked, if they are inconsistent, the set is empty, otherwise, the
- set contains only a vector of variables with the constraints in scope.
-
-* pmcheck :: PatVec -> [PatVec] -> ValVec -> PmM PartialResult
+* pmcheck :: PatVec -> [PatVec] -> ValVec -> Delta -> DsM PartialResult
- Checks redundancy, coverage and inaccessibility, using auxilary functions
- `pmcheckGuards` and `pmcheckHd`. Mainly handles the guard case which is
- common in all three checks (see paper) and calls `pmcheckGuards` when the
- whole clause is checked, or `pmcheckHd` when the pattern vector does not
- start with a guard.
+ This function implements functions `covered`, `uncovered` and
+ `divergent` from the paper at once. Calls out to the auxilary function
+ `pmcheckGuards` for handling (possibly multiple) guarded RHSs when the whole
+ clause is checked. Slightly different from the paper because it does not even
+ produce the covered and uncovered sets. Since we only care about whether a
+ clause covers SOMETHING or if it may forces ANY argument, we only store a
+ boolean in both cases, for efficiency.
-* pmcheckGuards :: [PatVec] -> ValVec -> PmM PartialResult
+* pmcheckGuards :: [PatVec] -> ValVec -> Delta -> DsM PartialResult
Processes the guards.
-
-* pmcheckHd :: Pattern -> PatVec -> [PatVec]
- -> ValAbs -> ValVec -> PmM PartialResult
-
- Worker: This function implements functions `covered`, `uncovered` and
- `divergent` from the paper at once. Slightly different from the paper because
- it does not even produce the covered and uncovered sets. Since we only care
- about whether a clause covers SOMETHING or if it may forces ANY argument, we
- only store a boolean in both cases, for efficiency.
-}
-- | Lift a pattern matching action from a single value vector abstration to a
--- value set abstraction, but calling it on every vector and the combining the
+-- value set abstraction, but calling it on every vector and combining the
-- results.
-runMany :: (ValVec -> PmM PartialResult) -> (Uncovered -> PmM PartialResult)
-runMany _ [] = return mempty
-runMany pm (m:ms) = mappend <$> pm m <*> runMany pm ms
-
--- | Generate the initial uncovered set. It initializes the
--- delta with all term and type constraints in scope.
-mkInitialUncovered :: [Id] -> PmM Uncovered
-mkInitialUncovered vars = do
- delta <- pmInitialTmTyCs
- let patterns = map PmVar vars
- return [ValVec patterns delta]
+runMany :: (Delta -> DsM PartialResult) -> Uncovered -> DsM PartialResult
+runMany _ [] = return emptyPartialResult
+runMany pm (m:ms) = do
+ res <- pm m
+ combinePartialResults res <$> runMany pm ms
-- | Increase the counter for elapsed algorithm iterations, check that the
-- limit is not exceeded and call `pmcheck`
-pmcheckI :: PatVec -> [PatVec] -> ValVec -> PmM PartialResult
-pmcheckI ps guards vva = do
- n <- liftD incrCheckPmIterDs
- tracePm "pmCheck" (ppr n <> colon <+> pprPatVec ps
- $$ hang (text "guards:") 2 (vcat (map pprPatVec guards))
- $$ pprValVecDebug vva)
- res <- pmcheck ps guards vva
+pmcheckI :: PatVec -> [PatVec] -> ValVec -> Int -> Delta -> DsM PartialResult
+pmcheckI ps guards vva n delta = do
+ m <- incrCheckPmIterDs
+ tracePm "pmCheck" (ppr m <> colon
+ $$ hang (text "patterns:") 2 (ppr ps)
+ $$ hang (text "guards:") 2 (ppr guards)
+ $$ ppr vva
+ $$ ppr delta)
+ res <- pmcheck ps guards vva n delta
tracePm "pmCheckResult:" (ppr res)
return res
{-# INLINE pmcheckI #-}
-- | Increase the counter for elapsed algorithm iterations, check that the
-- limit is not exceeded and call `pmcheckGuards`
-pmcheckGuardsI :: [PatVec] -> ValVec -> PmM PartialResult
-pmcheckGuardsI gvs vva = liftD incrCheckPmIterDs >> pmcheckGuards gvs vva
+pmcheckGuardsI :: [PatVec] -> Int -> Delta -> DsM PartialResult
+pmcheckGuardsI gvs n delta = incrCheckPmIterDs >> pmcheckGuards gvs n delta
{-# INLINE pmcheckGuardsI #-}
--- | Increase the counter for elapsed algorithm iterations, check that the
--- limit is not exceeded and call `pmcheckHd`
-pmcheckHdI :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec
- -> PmM PartialResult
-pmcheckHdI p ps guards va vva = do
- n <- liftD incrCheckPmIterDs
- tracePm "pmCheckHdI" (ppr n <> colon <+> pprPmPatDebug p
- $$ pprPatVec ps
- $$ hang (text "guards:") 2 (vcat (map pprPatVec guards))
- $$ pprPmPatDebug va
- $$ pprValVecDebug vva)
-
- res <- pmcheckHd p ps guards va vva
- tracePm "pmCheckHdI: res" (ppr res)
- return res
-{-# INLINE pmcheckHdI #-}
+-- | Check the list of mutually exclusive guards
+pmcheckGuards :: [PatVec] -> Int -> Delta -> DsM PartialResult
+pmcheckGuards [] _ delta = return (usimple delta)
+pmcheckGuards (gv:gvs) n delta = do
+ (PartialResult cs unc ds) <- pmcheckI gv [] [] n delta
+ let (n', unc')
+ -- See 6. in Note [Guards and Approximation]
+ | Just n' <- tryMultiplyDeltas (length unc) n = (n', unc)
+ | otherwise = (n, [delta])
+ (PartialResult css uncs dss) <- runMany (pmcheckGuardsI gvs n') unc'
+ return $ PartialResult (cs `mappend` css)
+ uncs
+ (ds `mappend` dss)
-- | Matching function: Check simultaneously a clause (takes separately the
-- patterns and the list of guards) for exhaustiveness, redundancy and
-- inaccessibility.
-pmcheck :: PatVec -> [PatVec] -> ValVec -> PmM PartialResult
-pmcheck [] guards vva@(ValVec [] _)
+pmcheck
+ :: PatVec -- ^ Patterns of the clause
+ -> [PatVec] -- ^ (Possibly multiple) guards of the clause
+ -> ValVec -- ^ The value vector abstraction to match against
+ -> Int -- ^ Estimate on the number of similar 'Delta's to handle.
+ -- See 6. in Note [Guards and Approximation]
+ -> Delta -- ^ Oracle state giving meaning to the identifiers in the ValVec
+ -> DsM PartialResult
+pmcheck [] guards [] n delta
| null guards = return $ mempty { presultCovered = Covered }
- | otherwise = pmcheckGuardsI guards vva
+ | otherwise = pmcheckGuardsI guards n delta
-- Guard
-pmcheck (PmFake : ps) guards vva =
+pmcheck (PmFake : ps) guards vva n delta =
-- short-circuit if the guard pattern is useless.
-- we just have two possible outcomes: fail here or match and recurse
-- none of the two contains any useful information about the failure
-- though. So just have these two cases but do not do all the boilerplate
- forces . mkCons vva <$> pmcheckI ps guards vva
-pmcheck (p : ps) guards (ValVec vas delta)
- | PmGrd { pm_grd_pv = pv, pm_grd_expr = e } <- p
- = do
- y <- liftD $ mkPmId (pmPatType p)
- let tm_state = extendSubst y e (delta_tm_cs delta)
- delta' = delta { delta_tm_cs = tm_state }
- utail <$> pmcheckI (pv ++ ps) guards (ValVec (PmVar y : vas) delta')
-
-pmcheck [] _ (ValVec (_:_) _) = panic "pmcheck: nil-cons"
-pmcheck (_:_) _ (ValVec [] _) = panic "pmcheck: cons-nil"
-
-pmcheck (p:ps) guards (ValVec (va:vva) delta)
- = pmcheckHdI p ps guards va (ValVec vva delta)
-
--- | Check the list of guards
-pmcheckGuards :: [PatVec] -> ValVec -> PmM PartialResult
-pmcheckGuards [] vva = return (usimple [vva])
-pmcheckGuards (gv:gvs) vva = do
- (PartialResult prov1 cs vsa ds) <- pmcheckI gv [] vva
- (PartialResult prov2 css vsas dss) <- runMany (pmcheckGuardsI gvs) vsa
- return $ PartialResult (prov1 `mappend` prov2)
- (cs `mappend` css)
- vsas
- (ds `mappend` dss)
-
--- | Worker function: Implements all cases described in the paper for all three
--- functions (`covered`, `uncovered` and `divergent`) apart from the `Guard`
--- cases which are handled by `pmcheck`
-pmcheckHd :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec
- -> PmM PartialResult
-
--- Var
-pmcheckHd (PmVar x) ps guards va (ValVec vva delta)
- | Just tm_state <- solveOneEq (delta_tm_cs delta) (TVC x (vaToPmExpr va))
- = ucon va <$> pmcheckI ps guards (ValVec vva (delta {delta_tm_cs = tm_state}))
- | otherwise = return mempty
-
--- ConCon
-pmcheckHd ( p@(PmCon { pm_con_con = c1, pm_con_tvs = ex_tvs1
- , pm_con_args = args1})) ps guards
- (va@(PmCon { pm_con_con = c2, pm_con_tvs = ex_tvs2
- , pm_con_args = args2})) (ValVec vva delta)
- | c1 /= c2 =
- return (usimple [ValVec (va:vva) delta])
- | otherwise = do
- let to_evvar tv1 tv2 = nameType "pmConCon" $
- mkPrimEqPred (mkTyVarTy tv1) (mkTyVarTy tv2)
- mb_to_evvar tv1 tv2
- -- If we have identical constructors but different existential
- -- tyvars, then generate extra equality constraints to ensure the
- -- existential tyvars.
- -- See Note [Coverage checking and existential tyvars].
- | tv1 == tv2 = pure Nothing
- | otherwise = Just <$> to_evvar tv1 tv2
- evvars <- (listToBag . catMaybes) <$>
- ASSERT(ex_tvs1 `equalLength` ex_tvs2)
- liftD (zipWithM mb_to_evvar ex_tvs1 ex_tvs2)
- let delta' = delta { delta_ty_cs = evvars `unionBags` delta_ty_cs delta }
- kcon c1 (pm_con_arg_tys p) (pm_con_tvs p) (pm_con_dicts p)
- <$> pmcheckI (args1 ++ ps) guards (ValVec (args2 ++ vva) delta')
-
--- LitLit
-pmcheckHd (PmLit l1) ps guards (va@(PmLit l2)) vva =
- case eqPmLit l1 l2 of
- True -> ucon va <$> pmcheckI ps guards vva
- False -> return $ ucon va (usimple [vva])
+ -- TODO: I don't think this should mkCons delta, rather than just replace the
+ -- presultUncovered by [delta] completely. Note that the uncovered set
+ -- returned from the recursive call can only be a refinement of the
+ -- original delta.
+ forces . mkCons delta <$> pmcheckI ps guards vva n delta
+pmcheck (p@PmGrd { pm_grd_pv = pv, pm_grd_expr = e } : ps) guards vva n delta = do
+ tracePm "PmGrd: pmPatType" (vcat [ppr p, ppr (pmPatType p)])
+ x <- mkPmId (exprType e)
+ delta' <- expectJust "x is fresh" <$> addVarCoreCt delta x e
+ pmcheckI (pv ++ ps) guards (x : vva) n delta'
+
+-- Var: Add x :-> y to the oracle and recurse
+pmcheck (PmVar x : ps) guards (y : vva) n delta = do
+ delta' <- expectJust "x is fresh" <$> addTmCt delta (TmVarVar x y)
+ pmcheckI ps guards vva n delta'
-- ConVar
-pmcheckHd (p@(PmCon { pm_con_con = con, pm_con_arg_tys = tys }))
- ps guards
- (PmVar x) (ValVec vva delta) = do
- (prov, complete_match) <- select =<< liftD (allCompleteMatches con tys)
-
- cons_cs <- mapM (liftD . mkOneConFull x tys) complete_match
-
- inst_vsa <- flip mapMaybeM cons_cs $
- \InhabitationCandidate{ ic_val_abs = va, ic_tm_ct = tm_ct
- , ic_ty_cs = ty_cs
- , ic_strict_arg_tys = strict_arg_tys } -> do
- mb_sat <- pmIsSatisfiable delta tm_ct ty_cs strict_arg_tys
- pure $ fmap (ValVec (va:vva)) mb_sat
-
- set_provenance prov .
- force_if (canDiverge (idName x) (delta_tm_cs delta)) <$>
- runMany (pmcheckI (p:ps) guards) inst_vsa
-
--- LitVar
-pmcheckHd (p@(PmLit l)) ps guards (PmVar x) (ValVec vva delta)
- = force_if (canDiverge (idName x) (delta_tm_cs delta)) <$>
- mkUnion non_matched <$>
- case solveOneEq (delta_tm_cs delta) (mkPosEq x l) of
- Just tm_state -> pmcheckHdI p ps guards (PmLit l) $
- ValVec vva (delta {delta_tm_cs = tm_state})
- Nothing -> return mempty
- where
- -- See Note [Refutable shapes] in TmOracle
- us | Just tm_state <- addSolveRefutableAltCon (delta_tm_cs delta) x (PmAltLit l)
- = [ValVec (PmNLit x [l] : vva) (delta { delta_tm_cs = tm_state })]
- | otherwise = []
-
- non_matched = usimple us
-
--- LitNLit
-pmcheckHd (p@(PmLit l)) ps guards
- (PmNLit { pm_lit_id = x, pm_lit_not = lits }) (ValVec vva delta)
- | all (not . eqPmLit l) lits
- , Just tm_state <- solveOneEq (delta_tm_cs delta) (mkPosEq x l)
- -- Both guards check the same so it would be sufficient to have only
- -- the second one. Nevertheless, it is much cheaper to check whether
- -- the literal is in the list so we check it first, to avoid calling
- -- the term oracle (`solveOneEq`) if possible
- = mkUnion non_matched <$>
- pmcheckHdI p ps guards (PmLit l)
- (ValVec vva (delta { delta_tm_cs = tm_state }))
- | otherwise = return non_matched
- where
- -- See Note [Refutable shapes] in TmOracle
- us | Just tm_state <- addSolveRefutableAltCon (delta_tm_cs delta) x (PmAltLit l)
- = [ValVec (PmNLit x (l:lits) : vva) (delta { delta_tm_cs = tm_state })]
- | otherwise = []
-
- non_matched = usimple us
-
--- ----------------------------------------------------------------------------
--- The following three can happen only in cases like #322 where constructors
--- and overloaded literals appear in the same match. The general strategy is
--- to replace the literal (positive/negative) by a variable and recurse. The
--- fact that the variable is equal to the literal is recorded in `delta` so
--- no information is lost
-
--- LitCon
-pmcheckHd p@PmLit{} ps guards va@PmCon{} (ValVec vva delta)
- = do y <- liftD $ mkPmId (pmPatType va)
- -- Analogous to the ConVar case, we have to case split the value
- -- abstraction on possible literals. We do so by introducing a fresh
- -- variable that is equated to the constructor. LitVar will then take
- -- care of the case split by resorting to NLit.
- let tm_state = extendSubst y (vaToPmExpr va) (delta_tm_cs delta)
- delta' = delta { delta_tm_cs = tm_state }
- pmcheckHdI p ps guards (PmVar y) (ValVec vva delta')
-
--- ConLit
-pmcheckHd p@PmCon{} ps guards (PmLit l) (ValVec vva delta)
- = do y <- liftD $ mkPmId (pmPatType p)
- -- This desugars to the ConVar case by introducing a fresh variable that
- -- is equated to the literal via a constraint. ConVar will then properly
- -- case split on all possible constructors.
- let tm_state = extendSubst y (PmExprLit l) (delta_tm_cs delta)
- delta' = delta { delta_tm_cs = tm_state }
- pmcheckHdI p ps guards (PmVar y) (ValVec vva delta')
-
--- ConNLit
-pmcheckHd (p@(PmCon {})) ps guards (PmNLit { pm_lit_id = x }) vva
- = pmcheckHdI p ps guards (PmVar x) vva
-
--- Impossible: handled by pmcheck
-pmcheckHd PmFake _ _ _ _ = panic "pmcheckHd: Fake"
-pmcheckHd (PmGrd {}) _ _ _ _ = panic "pmcheckHd: Guard"
-
-{-
-Note [Coverage checking and existential tyvars]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-GHC's implementation of the pattern-match coverage algorithm (as described in
-the GADTs Meet Their Match paper) must take some care to emit enough type
-constraints when handling data constructors with exisentially quantified type
-variables. To better explain what the challenge is, consider a constructor K
-of the form:
-
- K @e_1 ... @e_m ev_1 ... ev_v ty_1 ... ty_n :: T u_1 ... u_p
-
-Where:
-
-* e_1, ..., e_m are the existentially bound type variables.
-* ev_1, ..., ev_v are evidence variables, which may inhabit a dictionary type
- (e.g., Eq) or an equality constraint (e.g., e_1 ~ Int).
-* ty_1, ..., ty_n are the types of K's fields.
-* T u_1 ... u_p is the return type, where T is the data type constructor, and
- u_1, ..., u_p are the universally quantified type variables.
-
-In the ConVar case, the coverage algorithm will have in hand the constructor
-K as well as a list of type arguments [t_1, ..., t_n] to substitute T's
-universally quantified type variables u_1, ..., u_n for. It's crucial to take
-these in as arguments, as it is non-trivial to derive them just from the result
-type of a pattern synonym and the ambient type of the match (#11336, #17112).
-The type checker already did the hard work, so we should just make use of it.
-
-The presence of existentially quantified type variables adds a significant
-wrinkle. We always grab e_1, ..., e_m from the definition of K to begin with,
-but we don't want them to appear in the final PmCon, because then
-calling (mkOneConFull K) for other pattern variables might reuse the same
-existential tyvars, which is certainly wrong.
-
-Previously, GHC's solution to this wrinkle was to always create fresh names
-for the existential tyvars and put them into the PmCon. This works well for
-many cases, but it can break down if you nest GADT pattern matches in just
-the right way. For instance, consider the following program:
-
- data App f a where
- App :: f a -> App f (Maybe a)
-
- data Ty a where
- TBool :: Ty Bool
- TInt :: Ty Int
-
- data T f a where
- C :: T Ty (Maybe Bool)
-
- foo :: T f a -> App f a -> ()
- foo C (App TBool) = ()
-
-foo is a total program, but with the previous approach to handling existential
-tyvars, GHC would mark foo's patterns as non-exhaustive.
-
-When foo is desugared to Core, it looks roughly like so:
-
- foo @f @a (C co1 _co2) (App @a1 _co3 (TBool |> co1)) = ()
-
-(Where `a1` is an existential tyvar.)
-
-That, in turn, is processed by the coverage checker to become:
-
- foo @f @a (C co1 _co2) (App @a1 _co3 (pmvar123 :: f a1))
- | TBool <- pmvar123 |> co1
- = ()
-
-Note that the type of pmvar123 is `f a1`—this will be important later.
-
-Now, we proceed with coverage-checking as usual. When we come to the
-ConVar case for App, we create a fresh variable `a2` to represent its
-existential tyvar. At this point, we have the equality constraints
-`(a ~ Maybe a2, a ~ Maybe Bool, f ~ Ty)` in scope.
-
-However, when we check the guard, it will use the type of pmvar123, which is
-`f a1`. Thus, when considering if pmvar123 can match the constructor TInt,
-it will generate the constraint `a1 ~ Int`. This means our final set of
-equality constraints would be:
-
- f ~ Ty
- a ~ Maybe Bool
- a ~ Maybe a2
- a1 ~ Int
-
-Which is satisfiable! Freshening the existential tyvar `a` to `a2` doomed us,
-because GHC is unable to relate `a2` to `a1`, which really should be the same
-tyvar.
-
-Luckily, we can avoid this pitfall. Recall that the ConVar case was where we
-generated a PmCon with too-fresh existentials. But after ConVar, we have the
-ConCon case, which considers whether each constructor of a particular data type
-can be matched on in a particular spot.
-
-In the case of App, when we get to the ConCon case, we will compare our
-original App PmCon (from the source program) to the App PmCon created from the
-ConVar case. In the former PmCon, we have `a1` in hand, which is exactly the
-existential tyvar we want! Thus, we can force `a1` to be the same as `a2` here
-by emitting an additional `a1 ~ a2` constraint. Now our final set of equality
-constraints will be:
-
- f ~ Ty
- a ~ Maybe Bool
- a ~ Maybe a2
- a1 ~ Int
- a1 ~ a2
-
-Which is unsatisfiable, as we desired, since we now have that
-Int ~ a1 ~ a2 ~ Bool.
-
-In general, App might have more than one constructor, in which case we
-couldn't reuse the existential tyvar for App for a different constructor. This
-means that we can only use this trick in ConCon when the constructors are the
-same. But this is fine, since this is the only scenario where this situation
-arises in the first place!
--}
+pmcheck (p@PmCon{ pm_con_con = con, pm_con_args = args
+ , pm_con_arg_tys = arg_tys, pm_con_tvs = ex_tvs } : ps)
+ guards (x : vva) n delta = do
+ -- E.g f (K p q) = <rhs>
+ -- <next equation>
+ -- Split the value vector into two value vectors:
+ -- * one for <rhs>, binding x to (K p q)
+ -- * one for <next equation>, recording that x is /not/ (K _ _)
+
+ -- Stuff for <rhs>
+ pr_pos <- refineToAltCon delta x con arg_tys ex_tvs >>= \case
+ Nothing -> pure mempty
+ Just (delta', arg_vas) ->
+ pmcheckI (args ++ ps) guards (arg_vas ++ vva) n delta'
+
+ -- Stuff for <next equation>
+ -- The var is forced regardless of whether @con@ was satisfiable
+ let pr_pos' = forceIfCanDiverge delta x pr_pos
+ pr_neg <- addRefutableAltCon delta x con >>= \case
+ Nothing -> pure mempty
+ Just delta' -> pure (usimple delta')
+
+ tracePm "ConVar" (vcat [ppr p, ppr x, ppr pr_pos', ppr pr_neg])
+
+ -- Combine both into a single PartialResult
+ let pr = mkUnion pr_pos' pr_neg
+ case (presultUncovered pr_pos', presultUncovered pr_neg) of
+ ([], _) -> pure pr
+ (_, []) -> pure pr
+ -- See Note [Limit the number of refinements]
+ _ | lookupNumberOfRefinements delta x < mAX_REFINEMENTS
+ -> pure pr
+ | otherwise -> pure pr{ presultUncovered = [delta] }
+
+pmcheck [] _ (_:_) _ _ = panic "pmcheck: nil-cons"
+pmcheck (_:_) _ [] _ _ = panic "pmcheck: cons-nil"
-- ----------------------------------------------------------------------------
-- * Utilities for main checking
-updateVsa :: (ValSetAbs -> ValSetAbs) -> (PartialResult -> PartialResult)
-updateVsa f p@(PartialResult { presultUncovered = old })
+updateUncovered :: (Uncovered -> Uncovered) -> (PartialResult -> PartialResult)
+updateUncovered f p@(PartialResult { presultUncovered = old })
= p { presultUncovered = f old }
--- | Initialise with default values for covering and divergent information.
-usimple :: ValSetAbs -> PartialResult
-usimple vsa = mempty { presultUncovered = vsa }
-
--- | Take the tail of all value vector abstractions in the uncovered set
-utail :: PartialResult -> PartialResult
-utail = updateVsa upd
- where upd vsa = [ ValVec vva delta | ValVec (_:vva) delta <- vsa ]
-
--- | Prepend a value abstraction to all value vector abstractions in the
--- uncovered set
-ucon :: ValAbs -> PartialResult -> PartialResult
-ucon va = updateVsa upd
- where
- upd vsa = [ ValVec (va:vva) delta | ValVec vva delta <- vsa ]
-
--- | Given a data constructor of arity `a` and an uncovered set containing
--- value vector abstractions of length `(a+n)`, pass the first `n` value
--- abstractions to the constructor (Hence, the resulting value vector
--- abstractions will have length `n+1`)
-kcon :: ConLike -> [Type] -> [TyVar] -> [EvVar]
- -> PartialResult -> PartialResult
-kcon con arg_tys ex_tvs dicts
- = let n = conLikeArity con
- upd vsa =
- [ ValVec (va:vva) delta
- | ValVec vva' delta <- vsa
- , let (args, vva) = splitAt n vva'
- , let va = PmCon { pm_con_con = con
- , pm_con_arg_tys = arg_tys
- , pm_con_tvs = ex_tvs
- , pm_con_dicts = dicts
- , pm_con_args = args } ]
- in updateVsa upd
+-- | Initialise with default values for covering and divergent information and
+-- a singleton uncovered set.
+usimple :: Delta -> PartialResult
+usimple delta = mempty { presultUncovered = [delta] }
-- | Get the union of two covered, uncovered and divergent value set
-- abstractions. Since the covered and divergent sets are represented by a
@@ -2339,21 +1309,19 @@ kcon con arg_tys ex_tvs dicts
mkUnion :: PartialResult -> PartialResult -> PartialResult
mkUnion = mappend
--- | Add a value vector abstraction to a value set abstraction (uncovered).
-mkCons :: ValVec -> PartialResult -> PartialResult
-mkCons vva = updateVsa (vva:)
+-- | Add a model to the uncovered set.
+mkCons :: Delta -> PartialResult -> PartialResult
+mkCons model = updateUncovered (model:)
-- | Set the divergent set to not empty
forces :: PartialResult -> PartialResult
forces pres = pres { presultDivergent = Diverged }
--- | Set the divergent set to non-empty if the flag is `True`
-force_if :: Bool -> PartialResult -> PartialResult
-force_if True pres = forces pres
-force_if False pres = pres
-
-set_provenance :: Provenance -> PartialResult -> PartialResult
-set_provenance prov pr = pr { presultProvenance = prov }
+-- | Set the divergent set to non-empty if the variable has not been forced yet
+forceIfCanDiverge :: Delta -> Id -> PartialResult -> PartialResult
+forceIfCanDiverge delta x
+ | canDiverge delta x = forces
+ | otherwise = id
-- ----------------------------------------------------------------------------
-- * Propagation of term constraints inwards when checking nested matches
@@ -2362,7 +1330,7 @@ set_provenance prov pr = pr { presultProvenance = prov }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When checking a match it would be great to have all type and term information
available so we can get more precise results. For this reason we have functions
-`addDictsDs' and `addTmCsDs' in PmMonad that store in the environment type and
+`addDictsDs' and `addTmVarCsDs' in DsMonad that store in the environment type and
term constraints (respectively) as we go deeper.
The type constraints we propagate inwards are collected by `collectEvVarsPats'
@@ -2384,80 +1352,88 @@ f x = case x of
(_:_) -> True
[] -> False -- can't happen
-Functions `genCaseTmCs1' and `genCaseTmCs2' are responsible for generating
+Functions `addScrutTmCs' and `addPatTmCs' are responsible for generating
these constraints.
-}
--- | Generate equalities when checking a case expression:
--- case x of { p1 -> e1; ... pn -> en }
--- When we go deeper to check e.g. e1 we record two equalities:
--- (x ~ y), where y is the initial uncovered when checking (p1; .. ; pn)
--- and (x ~ p1).
-genCaseTmCs2 :: Maybe (LHsExpr GhcTc) -- Scrutinee
- -> [Pat GhcTc] -- LHS (should have length 1)
- -> [Id] -- MatchVars (should have length 1)
- -> DsM (Bag TmVarCt)
-genCaseTmCs2 Nothing _ _ = return emptyBag
-genCaseTmCs2 (Just scr) [p] [var] = do
- fam_insts <- dsGetFamInstEnvs
- [e] <- map vaToPmExpr . coercePatVec <$> translatePat fam_insts p
- let scr_e = lhsExprToPmExpr scr
- return $ listToBag [(TVC var e), (TVC var scr_e)]
-genCaseTmCs2 _ _ _ = panic "genCaseTmCs2: HsCase"
-
--- | Generate a simple equality when checking a case expression:
--- case x of { matches }
--- When checking matches we record that (x ~ y) where y is the initial
+locallyExtendPmDelta :: (Delta -> DsM (Maybe Delta)) -> DsM a -> DsM a
+locallyExtendPmDelta ext k = getPmDelta >>= ext >>= \case
+ -- If adding a constraint would lead to a contradiction, don't add it.
+ -- See @Note [Recovering from unsatisfiable pattern-matching constraints]@
+ -- for why this is done.
+ Nothing -> k
+ Just delta' -> updPmDelta delta' k
+
+-- | Add in-scope type constraints
+addTyCsDs :: Bag EvVar -> DsM a -> DsM a
+addTyCsDs ev_vars =
+ locallyExtendPmDelta (\delta -> addTypeEvidence delta ev_vars)
+
+-- | Add equalities for the scrutinee to the local 'DsM' environment when
+-- checking a case expression:
+-- case e of x { matches }
+-- When checking matches we record that (x ~ e) where x is the initial
-- uncovered. All matches will have to satisfy this equality.
-genCaseTmCs1 :: Maybe (LHsExpr GhcTc) -> [Id] -> Bag TmVarCt
-genCaseTmCs1 Nothing _ = emptyBag
-genCaseTmCs1 (Just scr) [var] = unitBag (TVC var (lhsExprToPmExpr scr))
-genCaseTmCs1 _ _ = panic "genCaseTmCs1: HsCase"
-
-{- Note [Literals in PmPat]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Instead of translating a literal to a variable accompanied with a guard, we
-treat them like constructor patterns. The following example from
-"./libraries/base/GHC/IO/Encoding.hs" shows why:
-
-mkTextEncoding' :: CodingFailureMode -> String -> IO TextEncoding
-mkTextEncoding' cfm enc = case [toUpper c | c <- enc, c /= '-'] of
- "UTF8" -> return $ UTF8.mkUTF8 cfm
- "UTF16" -> return $ UTF16.mkUTF16 cfm
- "UTF16LE" -> return $ UTF16.mkUTF16le cfm
- ...
-
-Each clause gets translated to a list of variables with an equal number of
-guards. For every guard we generate two cases (equals True/equals False) which
-means that we generate 2^n cases to feed the oracle with, where n is the sum of
-the length of all strings that appear in the patterns. For this particular
-example this means over 2^40 cases. Instead, by representing them like with
-constructor we get the following:
- 1. We exploit the common prefix with our representation of VSAs
- 2. We prune immediately non-reachable cases
- (e.g. False == (x == "U"), True == (x == "U"))
-
-Note [Translating As Patterns]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Instead of translating x@p as: x (p <- x)
-we instead translate it as: p (x <- coercePattern p)
-for performance reasons. For example:
-
- f x@True = 1
- f y@False = 2
-
-Gives the following with the first translation:
-
- x |> {x == False, x == y, y == True}
-
-If we use the second translation we get an empty set, independently of the
-oracle. Since the pattern `p' may contain guard patterns though, it cannot be
-used as an expression. That's why we call `coercePatVec' to drop the guard and
-`vaToPmExpr' to transform the value abstraction to an expression in the
-guard pattern (value abstractions are a subset of expressions). We keep the
-guards in the first pattern `p' though.
+addScrutTmCs :: Maybe (LHsExpr GhcTc) -> [Id] -> DsM a -> DsM a
+addScrutTmCs Nothing _ k = k
+addScrutTmCs (Just scr) [x] k = do
+ scr_e <- dsLExpr scr
+ locallyExtendPmDelta (\delta -> addVarCoreCt delta x scr_e) k
+addScrutTmCs _ _ _ = panic "addScrutTmCs: HsCase with more than one case binder"
+
+-- | Add equalities to the local 'DsM' environment when checking the RHS of a
+-- case expression:
+-- case e of x { p1 -> e1; ... pn -> en }
+-- When we go deeper to check e.g. e1 we record (x ~ p1).
+addPatTmCs :: [Pat GhcTc] -- LHS (should have length 1)
+ -> [Id] -- MatchVars (should have length 1)
+ -> DsM a
+ -> DsM a
+-- Morally, this computes an approximation of the Covered set for p1
+-- (which pmcheck currently discards). TODO: Re-use pmcheck instead of calling
+-- out to awkard addVarPatVecCt.
+addPatTmCs ps xs k = do
+ fam_insts <- dsGetFamInstEnvs
+ pv <- concat <$> translatePatVec fam_insts ps
+ locallyExtendPmDelta (\delta -> addVarPatVecCt delta xs pv) k
+
+-- ----------------------------------------------------------------------------
+-- * Converting between Value Abstractions, Patterns and PmExpr
+-- | Add a constraint equating a variable to a 'PatVec'. Picks out the single
+-- 'PmPat' of arity 1 and equates x to it. Returns the original Delta if that
+-- fails. Otherwise it returns Nothing when the resulting Delta would be
+-- unsatisfiable, or @Just delta'@ when the extended @delta'@ is still possibly
+-- satisfiable.
+addVarPatVecCt :: Delta -> [Id] -> PatVec -> DsM (Maybe Delta)
+-- This is just a simple version of pmcheck to compute the Covered Delta
+-- (which pmcheck doesn't even attempt to keep).
+-- Also PmGrd, although having pattern arity 0, really stores important info.
+-- For example, as-patterns desugar to a plain variable match and an associated
+-- PmGrd for the RHS of the @. We don't currently look into that PmGrd and I'm
+-- not willing to duplicate any more of pmcheck.
+addVarPatVecCt delta (x:xs) (pat:pv)
+ | patternArity pat == 1 -- PmVar or PmCon
+ = runMaybeT $ do
+ delta' <- MaybeT (addVarPatCt delta x pat)
+ MaybeT (addVarPatVecCt delta' xs pv)
+ | otherwise -- PmGrd or PmFake
+ = addVarPatVecCt delta (x:xs) pv
+addVarPatVecCt delta [] pv = ASSERT( patVecArity pv == 0 ) pure (Just delta)
+addVarPatVecCt _ (_:_) [] = panic "More match vars than patterns"
+
+-- | Convert a pattern to a 'PmExpr' (will be either 'Nothing' if the pattern is
+-- a guard pattern, or 'Just' an expression in all other cases) by dropping the
+-- guards
+addVarPatCt :: Delta -> Id -> PmPat -> DsM (Maybe Delta)
+addVarPatCt delta x (PmVar { pm_var_id = y }) = addTmCt delta (TmVarVar x y)
+addVarPatCt delta x (PmCon { pm_con_con = con, pm_con_args = args }) = runMaybeT $ do
+ arg_ids <- traverse (lift . mkPmId . pmPatType) args
+ delta' <- foldlM (\delta (y, arg) -> MaybeT (addVarPatCt delta y arg)) delta (zip arg_ids args)
+ MaybeT (addTmCt delta' (TmVarCon x con arg_ids))
+addVarPatCt delta _ _pat = ASSERT( patternArity _pat == 0 ) pure (Just delta)
+{-
%************************************************************************
%* *
Pretty printing of exhaustiveness/redundancy check warnings
@@ -2465,32 +1441,34 @@ guards in the first pattern `p' though.
%************************************************************************
-}
--- | Check whether any part of pattern match checking is enabled (does not
--- matter whether it is the redundancy check or the exhaustiveness check).
-isAnyPmCheckEnabled :: DynFlags -> DsMatchContext -> Bool
-isAnyPmCheckEnabled dflags (DsMatchContext kind _loc)
+-- | Check whether any part of pattern match checking is enabled for this
+-- 'HsMatchContext' (does not matter whether it is the redundancy check or the
+-- exhaustiveness check).
+isMatchContextPmChecked :: DynFlags -> Origin -> HsMatchContext id -> Bool
+isMatchContextPmChecked dflags origin kind
+ | isGenerated origin
+ = False
+ | otherwise
= wopt Opt_WarnOverlappingPatterns dflags || exhaustive dflags kind
-instance Outputable ValVec where
- ppr (ValVec vva delta)
- = let (subst, refuts) = wrapUpTmState (delta_tm_cs delta)
- vector = substInValAbs subst vva
- in pprUncovered (vector, refuts)
-
--- | Apply a term substitution to a value vector abstraction. All VAs are
--- transformed to PmExpr (used only before pretty printing).
-substInValAbs :: TmVarCtEnv -> [ValAbs] -> [PmExpr]
-substInValAbs subst = map (exprDeepLookup subst . vaToPmExpr)
+-- | Return True when any of the pattern match warnings ('allPmCheckWarnings')
+-- are enabled, in which case we need to run the pattern match checker.
+needToRunPmCheck :: DynFlags -> Origin -> Bool
+needToRunPmCheck dflags origin
+ | isGenerated origin
+ = False
+ | otherwise
+ = notNull (filter (`wopt` dflags) allPmCheckWarnings)
-- | Issue all the warnings (coverage, exhaustiveness, inaccessibility)
dsPmWarn :: DynFlags -> DsMatchContext -> PmResult -> DsM ()
dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
= when (flag_i || flag_u) $ do
- let exists_r = flag_i && notNull redundant && onlyBuiltin
- exists_i = flag_i && notNull inaccessible && onlyBuiltin && not is_rec_upd
+ let exists_r = flag_i && notNull redundant
+ exists_i = flag_i && notNull inaccessible && not is_rec_upd
exists_u = flag_u && (case uncovered of
- TypeOfUncovered _ -> True
- UncoveredPatterns u -> notNull u)
+ TypeOfUncovered _ -> True
+ UncoveredPatterns _ unc -> notNull unc)
when exists_r $ forM_ redundant $ \(dL->L l q) -> do
putSrcSpanDs l (warnDs (Reason Opt_WarnOverlappingPatterns)
@@ -2500,12 +1478,11 @@ dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
(pprEqn q "has inaccessible right hand side"))
when exists_u $ putSrcSpanDs loc $ warnDs flag_u_reason $
case uncovered of
- TypeOfUncovered ty -> warnEmptyCase ty
- UncoveredPatterns candidates -> pprEqns candidates
+ TypeOfUncovered ty -> warnEmptyCase ty
+ UncoveredPatterns vars unc -> pprEqns vars unc
where
PmResult
- { pmresultProvenance = prov
- , pmresultRedundant = redundant
+ { pmresultRedundant = redundant
, pmresultUncovered = uncovered
, pmresultInaccessible = inaccessible } = pm_result
@@ -2516,8 +1493,6 @@ dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
is_rec_upd = case kind of { RecUpd -> True; _ -> False }
-- See Note [Inaccessible warnings for record updates]
- onlyBuiltin = prov == FromBuiltin
-
maxPatterns = maxUncoveredPatterns dflags
-- Print a single clause (for redundant/with-inaccessible-rhs)
@@ -2525,14 +1500,12 @@ dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
f (pprPats kind (map unLoc q))
-- Print several clauses (for uncovered clauses)
- pprEqns qs = pprContext False ctx (text "are non-exhaustive") $ \_ ->
- case qs of -- See #11245
- [ValVec [] _]
- -> text "Guards do not cover entire pattern space"
- _missing -> let us = map ppr qs
- in hang (text "Patterns not matched:") 4
- (vcat (take maxPatterns us)
- $$ dots maxPatterns us)
+ pprEqns vars deltas = pprContext False ctx (text "are non-exhaustive") $ \_ ->
+ case vars of -- See #11245
+ [] -> text "Guards do not cover entire pattern space"
+ _ -> let us = map (\delta -> pprUncovered delta vars) deltas
+ in hang (text "Patterns not matched:") 4
+ (vcat (take maxPatterns us) $$ dots maxPatterns us)
-- Print a type-annotated wildcard (for non-exhaustive `EmptyCase`s for
-- which we only know the type and have no inhabitants at hand)
@@ -2583,6 +1556,15 @@ dots maxPatterns qs
| qs `lengthExceeds` maxPatterns = text "..."
| otherwise = empty
+-- | All warning flags that need to run the pattern match checker.
+allPmCheckWarnings :: [WarningFlag]
+allPmCheckWarnings =
+ [ Opt_WarnIncompletePatterns
+ , Opt_WarnIncompleteUniPatterns
+ , Opt_WarnIncompletePatternsRecUpd
+ , Opt_WarnOverlappingPatterns
+ ]
+
-- | Check whether the exhaustiveness checker should run (exhaustiveness only)
exhaustive :: DynFlags -> HsMatchContext id -> Bool
exhaustive dflags = maybe False (`wopt` dflags) . exhaustiveWarningFlag
@@ -2626,43 +1608,3 @@ pprContext singular (DsMatchContext kind _loc) msg rest_of_msg_fun
pprPats :: HsMatchContext Name -> [Pat GhcTc] -> SDoc
pprPats kind pats
= sep [sep (map ppr pats), matchSeparator kind, text "..."]
-
--- Debugging Infrastructre
-
-tracePm :: String -> SDoc -> PmM ()
-tracePm herald doc = liftD $ tracePmD herald doc
-
-
-tracePmD :: String -> SDoc -> DsM ()
-tracePmD herald doc = do
- dflags <- getDynFlags
- printer <- mkPrintUnqualifiedDs
- liftIO $ dumpIfSet_dyn_printer printer dflags
- Opt_D_dump_ec_trace (text herald $$ (nest 2 doc))
-
-
-pprPmPatDebug :: PmPat a -> SDoc
-pprPmPatDebug (PmCon cc _arg_tys _con_tvs _con_dicts con_args)
- = hsep [text "PmCon", ppr cc, hsep (map pprPmPatDebug con_args)]
-pprPmPatDebug (PmVar vid) = text "PmVar" <+> ppr vid
-pprPmPatDebug (PmLit li) = text "PmLit" <+> ppr li
-pprPmPatDebug (PmNLit i nl) = text "PmNLit" <+> ppr i <+> ppr nl
-pprPmPatDebug (PmGrd pv ge) = text "PmGrd" <+> hsep (map pprPmPatDebug pv)
- <+> ppr ge
-pprPmPatDebug PmFake = text "PmFake"
-
-pprPatVec :: PatVec -> SDoc
-pprPatVec ps = hang (text "Pattern:") 2
- (brackets $ sep
- $ punctuate (comma <> char '\n') (map pprPmPatDebug ps))
-
-pprValAbs :: [ValAbs] -> SDoc
-pprValAbs ps = hang (text "ValAbs:") 2
- (brackets $ sep
- $ punctuate (comma) (map pprPmPatDebug ps))
-
-pprValVecDebug :: ValVec -> SDoc
-pprValVecDebug (ValVec vas _d) = text "ValVec" <+>
- parens (pprValAbs vas)
- -- <not a haddock> $$ ppr (delta_tm_cs _d)
- -- <not a haddock> $$ ppr (delta_ty_cs _d)