diff options
73 files changed, 3885 insertions, 2582 deletions
diff --git a/compiler/basicTypes/NameEnv.hs b/compiler/basicTypes/NameEnv.hs index 0b1cf435bc..5f6bdc5846 100644 --- a/compiler/basicTypes/NameEnv.hs +++ b/compiler/basicTypes/NameEnv.hs @@ -25,9 +25,9 @@ module NameEnv ( emptyDNameEnv, lookupDNameEnv, - delFromDNameEnv, + delFromDNameEnv, filterDNameEnv, mapDNameEnv, - alterDNameEnv, + adjustDNameEnv, alterDNameEnv, extendDNameEnv, -- ** Dependency analysis depAnal ) where @@ -151,8 +151,17 @@ lookupDNameEnv = lookupUDFM delFromDNameEnv :: DNameEnv a -> Name -> DNameEnv a delFromDNameEnv = delFromUDFM +filterDNameEnv :: (a -> Bool) -> DNameEnv a -> DNameEnv a +filterDNameEnv = filterUDFM + mapDNameEnv :: (a -> b) -> DNameEnv a -> DNameEnv b mapDNameEnv = mapUDFM +adjustDNameEnv :: (a -> a) -> DNameEnv a -> Name -> DNameEnv a +adjustDNameEnv = adjustUDFM + alterDNameEnv :: (Maybe a -> Maybe a) -> DNameEnv a -> Name -> DNameEnv a alterDNameEnv = alterUDFM + +extendDNameEnv :: DNameEnv a -> Name -> a -> DNameEnv a +extendDNameEnv = addToUDFM diff --git a/compiler/basicTypes/PatSyn.hs b/compiler/basicTypes/PatSyn.hs index 008a0771fa..14a05b3258 100644 --- a/compiler/basicTypes/PatSyn.hs +++ b/compiler/basicTypes/PatSyn.hs @@ -184,6 +184,20 @@ to get the instantiation a := ty. This is very unlike DataCons, where univ tyvars match 1-1 the arguments of the TyCon. +Side note: I (SG) get the impression that instantiated return types should +generate a *required* constraint for pattern synonyms, rather than a *provided* +constraint like it's the case for GADTs. For example, I'd expect these +declarations to have identical semantics: + + pattern Just42 :: Maybe Int + pattern Just42 = Just 42 + + pattern Just'42 :: (a ~ Int) => Maybe a + pattern Just'42 = Just 42 + +The latter generates the proper required constraint, the former does not. +Also rather different to GADTs is the fact that Just42 doesn't have any +universally quantified type variables, whereas Just'42 or MkS above has. Note [Pattern synonym representation] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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) diff --git a/compiler/deSugar/DsBinds.hs b/compiler/deSugar/DsBinds.hs index 4a6a463b2d..cea7f3215b 100644 --- a/compiler/deSugar/DsBinds.hs +++ b/compiler/deSugar/DsBinds.hs @@ -29,7 +29,7 @@ import {-# SOURCE #-} Match( matchWrapper ) import DsMonad import DsGRHSs import DsUtils -import Check ( checkGuardMatches ) +import Check ( needToRunPmCheck, addTyCsDs, checkGuardMatches ) import HsSyn -- lots of things import CoreSyn -- lots of things @@ -186,11 +186,15 @@ dsHsBind dflags (AbsBinds { abs_tvs = tyvars, abs_ev_vars = dicts , abs_exports = exports , abs_ev_binds = ev_binds , abs_binds = binds, abs_sig = has_sig }) - = do { ds_binds <- addDictsDs (listToBag dicts) $ - dsLHsBinds binds - -- addDictsDs: push type constraints deeper - -- for inner pattern match check - -- See Check, Note [Type and Term Equality Propagation] + = do { ds_binds <- applyWhen (needToRunPmCheck dflags FromSource) + -- FromSource might not be accurate, but at worst + -- we do superfluous calls to the pattern match + -- oracle. + -- addTyCsDs: push type constraints deeper + -- for inner pattern match check + -- See Check, Note [Type and Term Equality Propagation] + (addTyCsDs (listToBag dicts)) + (dsLHsBinds binds) ; ds_ev_binds <- dsTcEvBinds_s ev_binds diff --git a/compiler/deSugar/DsGRHSs.hs b/compiler/deSugar/DsGRHSs.hs index 5adc661388..b0d35d0b2a 100644 --- a/compiler/deSugar/DsGRHSs.hs +++ b/compiler/deSugar/DsGRHSs.hs @@ -23,7 +23,9 @@ import MkCore import CoreSyn import CoreUtils (bindNonRec) -import Check (genCaseTmCs2) +import BasicTypes (Origin(FromSource)) +import DynFlags +import Check (needToRunPmCheck, addTyCsDs, addPatTmCs, addScrutTmCs) import DsMonad import DsUtils import Type ( Type ) @@ -122,11 +124,16 @@ matchGuards (BindStmt _ pat bind_rhs _ _ : stmts) ctx rhs rhs_ty = do let upat = unLoc pat dicts = collectEvVarsPat upat match_var <- selectMatchVar upat - tm_cs <- genCaseTmCs2 (Just bind_rhs) [upat] [match_var] - match_result <- addDictsDs dicts $ - addTmCsDs tm_cs $ - -- See Note [Type and Term Equality Propagation] in Check - matchGuards stmts ctx rhs rhs_ty + + dflags <- getDynFlags + match_result <- + -- See Note [Type and Term Equality Propagation] in Check + applyWhen (needToRunPmCheck dflags FromSource) + -- FromSource might not be accurate, but at worst + -- we do superfluous calls to the pattern match + -- oracle. + (addTyCsDs dicts . addScrutTmCs (Just bind_rhs) [match_var] . addPatTmCs [upat] [match_var]) + (matchGuards stmts ctx rhs rhs_ty) core_rhs <- dsLExpr bind_rhs match_result' <- matchSinglePatVar match_var (StmtCtxt ctx) pat rhs_ty match_result diff --git a/compiler/deSugar/DsMonad.hs b/compiler/deSugar/DsMonad.hs index 9534b4e20b..d937b3b134 100644 --- a/compiler/deSugar/DsMonad.hs +++ b/compiler/deSugar/DsMonad.hs @@ -29,8 +29,8 @@ module DsMonad ( DsMetaEnv, DsMetaVal(..), dsGetMetaEnv, dsLookupMetaEnv, dsExtendMetaEnv, - -- Getting and setting EvVars and term constraints in local environment - getDictsDs, addDictsDs, getTmCsDs, addTmCsDs, + -- Getting and setting pattern match oracle states + getPmDelta, updPmDelta, -- Iterations for pm checking incrCheckPmIterDs, resetPmIterDs, dsGetCompleteMatches, @@ -70,7 +70,7 @@ import BasicTypes ( Origin ) import DataCon import ConLike import TyCon -import PmExpr +import {-# SOURCE #-} PmOracle import Id import Module import Outputable @@ -82,7 +82,6 @@ import NameEnv import DynFlags import ErrUtils import FastString -import Var (EvVar) import UniqFM ( lookupWithDefaultUFM ) import Literal ( mkLitString ) import CostCentreState @@ -285,8 +284,7 @@ mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var pmvar cc_st_var } lcl_env = DsLclEnv { dsl_meta = emptyNameEnv , dsl_loc = real_span - , dsl_dicts = emptyBag - , dsl_tm_cs = emptyBag + , dsl_delta = initDelta , dsl_pm_iter = pmvar } in (gbl_env, lcl_env) @@ -386,23 +384,14 @@ the @SrcSpan@ being carried around. getGhcModeDs :: DsM GhcMode getGhcModeDs = getDynFlags >>= return . ghcMode --- | Get in-scope type constraints (pm check) -getDictsDs :: DsM (Bag EvVar) -getDictsDs = do { env <- getLclEnv; return (dsl_dicts env) } +-- | Get the current pattern match oracle state. See 'dsl_delta'. +getPmDelta :: DsM Delta +getPmDelta = do { env <- getLclEnv; return (dsl_delta env) } --- | Add in-scope type constraints (pm check) -addDictsDs :: Bag EvVar -> DsM a -> DsM a -addDictsDs ev_vars - = updLclEnv (\env -> env { dsl_dicts = unionBags ev_vars (dsl_dicts env) }) - --- | Get in-scope term constraints (pm check) -getTmCsDs :: DsM (Bag TmVarCt) -getTmCsDs = do { env <- getLclEnv; return (dsl_tm_cs env) } - --- | Add in-scope term constraints (pm check) -addTmCsDs :: Bag TmVarCt -> DsM a -> DsM a -addTmCsDs tm_cs - = updLclEnv (\env -> env { dsl_tm_cs = unionBags tm_cs (dsl_tm_cs env) }) +-- | Set the pattern match oracle state within the scope of the given action. +-- See 'dsl_delta'. +updPmDelta :: Delta -> DsM a -> DsM a +updPmDelta delta = updLclEnv (\env -> env { dsl_delta = delta }) -- | Increase the counter for elapsed pattern match check iterations. -- If the current counter is already over the limit, fail diff --git a/compiler/deSugar/ExtractDocs.hs b/compiler/deSugar/ExtractDocs.hs index ce5299443b..4d7f115074 100644 --- a/compiler/deSugar/ExtractDocs.hs +++ b/compiler/deSugar/ExtractDocs.hs @@ -289,7 +289,7 @@ ungroup group_ = mkDecls (typesigs . hs_valds) (SigD noExtField) group_ ++ mkDecls (valbinds . hs_valds) (ValD noExtField) group_ where - typesigs (XValBindsLR (NValBinds _ sigs)) = filter (isUserSig . unLoc) sigs + typesigs (XValBindsLR (NValBinds _ sig)) = filter (isUserSig . unLoc) sig typesigs ValBinds{} = error "expected XValBindsLR" valbinds (XValBindsLR (NValBinds binds _)) = diff --git a/compiler/deSugar/Match.hs b/compiler/deSugar/Match.hs index 921b829fb9..a0576494a0 100644 --- a/compiler/deSugar/Match.hs +++ b/compiler/deSugar/Match.hs @@ -690,7 +690,13 @@ Call @match@ with all of this information! matchWrapper :: HsMatchContext Name -- ^ For shadowing warning messages - -> Maybe (LHsExpr GhcTc) -- ^ Scrutinee, if we check a case expr + -> Maybe (LHsExpr GhcTc) -- ^ Scrutinee. (Just scrut) for a case expr + -- case scrut of { p1 -> e1 ... } + -- (and in this case the MatchGroup will + -- have all singleton patterns) + -- Nothing for a function definition + -- f p1 q1 = ... -- No "scrutinee" + -- f p2 q2 = ... -- in this case -> MatchGroup GhcTc (LHsExpr GhcTc) -- ^ Matches being desugared -> DsM ([Id], CoreExpr) -- ^ Results (usually passed to 'match') @@ -730,25 +736,30 @@ matchWrapper ctxt mb_scr (MG { mg_alts = (dL->L _ matches) ; eqns_info <- mapM (mk_eqn_info new_vars) matches - -- pattern match check warnings - ; unless (isGenerated origin) $ - when (isAnyPmCheckEnabled dflags (DsMatchContext ctxt locn)) $ - addTmCsDs (genCaseTmCs1 mb_scr new_vars) $ - -- See Note [Type and Term Equality Propagation] - checkMatches dflags (DsMatchContext ctxt locn) new_vars matches + -- Pattern match check warnings for /this match-group/ + ; when (isMatchContextPmChecked dflags origin ctxt) $ + addScrutTmCs mb_scr new_vars $ + -- See Note [Type and Term Equality Propagation] + checkMatches dflags (DsMatchContext ctxt locn) new_vars matches ; result_expr <- handleWarnings $ matchEquations ctxt new_vars eqns_info rhs_ty ; return (new_vars, result_expr) } where + -- Called once per equation in the match, or alternative in the case mk_eqn_info vars (dL->L _ (Match { m_pats = pats, m_grhss = grhss })) = do { dflags <- getDynFlags ; let upats = map (unLoc . decideBangHood dflags) pats dicts = collectEvVarsPats upats - ; tm_cs <- genCaseTmCs2 mb_scr upats vars - ; match_result <- addDictsDs dicts $ -- See Note [Type and Term Equality Propagation] - addTmCsDs tm_cs $ -- See Note [Type and Term Equality Propagation] - dsGRHSs ctxt grhss rhs_ty + + ; match_result <- + -- Extend the environment with knowledge about + -- the matches before desguaring the RHS + -- See Note [Type and Term Equality Propagation] + applyWhen (needToRunPmCheck dflags origin) + (addTyCsDs dicts . addScrutTmCs mb_scr vars . addPatTmCs upats vars) + (dsGRHSs ctxt grhss rhs_ty) + ; return (EqnInfo { eqn_pats = upats , eqn_orig = FromSource , eqn_rhs = match_result }) } diff --git a/compiler/deSugar/PmExpr.hs b/compiler/deSugar/PmExpr.hs index 3697dd8cc1..f83c63e4fd 100644 --- a/compiler/deSugar/PmExpr.hs +++ b/compiler/deSugar/PmExpr.hs @@ -6,29 +6,38 @@ Haskell expressions (as used by the pattern matching checker) and utilities. {-# LANGUAGE CPP #-} {-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} module PmExpr ( - PmExpr(..), PmLit(..), PmAltCon(..), TmVarCt(..), - eqPmLit, isNotPmExprOther, lhsExprToPmExpr, hsExprToPmExpr + PmLit(..), PmLitValue(..), PmAltCon(..), + pmAltConType, PmEquality(..), eqPmAltCon, + pmLitType, literalToPmLit, negatePmLit, overloadPmLit, + pmLitAsStringLit, coreExprAsPmLit ) where #include "HsVersions.h" import GhcPrelude -import BasicTypes (SourceText) -import FastString (FastString, unpackFS) -import HsSyn +import Util +import FastString import Id import Name import DataCon import ConLike -import TcEvidence (isErasableHsWrapper) -import TcType (isStringTy) -import TysWiredIn import Outputable -import SrcLoc +import Maybes +import Type +import TyCon +import Literal +import CoreSyn +import CoreUtils (exprType) +import PrelNames +import TysWiredIn +import TysPrim + +import Numeric (fromRat) +import Data.Foldable (find) +import Data.Ratio {- %************************************************************************ @@ -38,52 +47,135 @@ import SrcLoc %************************************************************************ -} -{- Note [PmExprOther in PmExpr] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Since there is no plan to extend the (currently pretty naive) term oracle in -the near future, instead of playing with the verbose (HsExpr Id), we lift it to -PmExpr. All expressions the term oracle does not handle are wrapped by the -constructor PmExprOther. Note that we do not perform substitution in -PmExprOther. Because of this, we do not even print PmExprOther, since they may -refer to variables that are otherwise substituted away. --} - -- ---------------------------------------------------------------------------- -- ** Types --- | Lifted expressions for pattern match checking. -data PmExpr = PmExprVar Name - | PmExprCon ConLike [PmExpr] - | PmExprLit PmLit - | PmExprOther (HsExpr GhcTc) -- Note [PmExprOther in PmExpr] - - -mkPmExprData :: DataCon -> [PmExpr] -> PmExpr -mkPmExprData dc args = PmExprCon (RealDataCon dc) args - -- | Literals (simple and overloaded ones) for pattern match checking. -data PmLit = PmSLit (HsLit GhcTc) -- simple - | PmOLit Bool {- is it negated? -} (HsOverLit GhcTc) -- overloaded - --- | Equality between literals for pattern match checking. -eqPmLit :: PmLit -> PmLit -> Bool -eqPmLit (PmSLit l1) (PmSLit l2) = l1 == l2 -eqPmLit (PmOLit b1 l1) (PmOLit b2 l2) = b1 == b2 && l1 == l2 - -- See Note [Undecidable Equality for Overloaded Literals] -eqPmLit _ _ = False - --- | Represents a match against a literal. We mostly use it to to encode shapes --- for a variable that immediately lead to a refutation. -- --- See Note [Refutable shapes] in TmOracle. Really similar to 'CoreSyn.AltCon'. -newtype PmAltCon = PmAltLit PmLit - deriving Outputable - +-- See Note [Undecidable Equality for PmAltCons] +data PmLit = PmLit + { pm_lit_ty :: Type + , pm_lit_val :: PmLitValue } + +data PmLitValue + = PmLitInt Integer + | PmLitRat Rational + | PmLitChar Char + -- We won't actually see PmLitString in the oracle since we desugar strings to + -- lists + | PmLitString FastString + | PmLitOverInt Int {- How often Negated? -} Integer + | PmLitOverRat Int {- How often Negated? -} Rational + | PmLitOverString FastString + +-- | Undecidable semantic equality result. +-- See Note [Undecidable Equality for PmAltCons] +data PmEquality + = Equal + | Disjoint + | PossiblyOverlap + deriving (Eq, Show) + +-- | When 'PmEquality' can be decided. @True <=> Equal@, @False <=> Disjoint@. +decEquality :: Bool -> PmEquality +decEquality True = Equal +decEquality False = Disjoint + +-- | Undecidable equality for values represented by 'PmLit's. +-- See Note [Undecidable Equality for PmAltCons] +-- +-- * @Just True@ ==> Surely equal +-- * @Just False@ ==> Surely different (non-overlapping, even!) +-- * @Nothing@ ==> Equality relation undecidable +eqPmLit :: PmLit -> PmLit -> PmEquality +eqPmLit (PmLit t1 v1) (PmLit t2 v2) + -- no haddock | pprTrace "eqPmLit" (ppr t1 <+> ppr v1 $$ ppr t2 <+> ppr v2) False = undefined + | not (t1 `eqType` t2) = Disjoint + | otherwise = go v1 v2 + where + go (PmLitInt i1) (PmLitInt i2) = decEquality (i1 == i2) + go (PmLitRat r1) (PmLitRat r2) = decEquality (r1 == r2) + go (PmLitChar c1) (PmLitChar c2) = decEquality (c1 == c2) + go (PmLitString s1) (PmLitString s2) = decEquality (s1 == s2) + go (PmLitOverInt n1 i1) (PmLitOverInt n2 i2) + | n1 == n2 && i1 == i2 = Equal + go (PmLitOverRat n1 r1) (PmLitOverRat n2 r2) + | n1 == n2 && r1 == r2 = Equal + go (PmLitOverString s1) (PmLitOverString s2) + | s1 == s2 = Equal + go _ _ = PossiblyOverlap + +-- | Syntactic equality. +instance Eq PmLit where + a == b = eqPmLit a b == Equal + +-- | Type of a 'PmLit' +pmLitType :: PmLit -> Type +pmLitType (PmLit ty _) = ty + +-- | Type of a 'PmAltCon' +pmAltConType :: PmAltCon -> [Type] -> Type +pmAltConType (PmAltLit lit) _arg_tys = ASSERT( null _arg_tys ) pmLitType lit +pmAltConType (PmAltConLike con) arg_tys = conLikeResTy con arg_tys + +-- | Undecidable equality for values represented by 'ConLike's. +-- See Note [Undecidable Equality for PmAltCons]. +-- 'PatSynCon's aren't enforced to be generative, so two syntactically different +-- 'PatSynCon's might match the exact same values. Without looking into and +-- reasoning about the pattern synonym's definition, we can't decide if their +-- sets of matched values is different. +-- +-- * @Just True@ ==> Surely equal +-- * @Just False@ ==> Surely different (non-overlapping, even!) +-- * @Nothing@ ==> Equality relation undecidable +eqConLike :: ConLike -> ConLike -> PmEquality +eqConLike (RealDataCon dc1) (RealDataCon dc2) = decEquality (dc1 == dc2) +eqConLike (PatSynCon psc1) (PatSynCon psc2) + | psc1 == psc2 + = Equal +eqConLike _ _ = PossiblyOverlap + +-- | Represents the head of a match against a 'ConLike' or literal. +-- Really similar to 'CoreSyn.AltCon'. +data PmAltCon = PmAltConLike ConLike + | PmAltLit PmLit + +-- | We can't in general decide whether two 'PmAltCon's match the same set of +-- values. In addition to the reasons in 'eqPmLit' and 'eqConLike', a +-- 'PmAltConLike' might or might not represent the same value as a 'PmAltLit'. +-- See Note [Undecidable Equality for PmAltCons]. +-- +-- * @Just True@ ==> Surely equal +-- * @Just False@ ==> Surely different (non-overlapping, even!) +-- * @Nothing@ ==> Equality relation undecidable +-- +-- Examples (omitting some constructor wrapping): +-- +-- * @eqPmAltCon (LitInt 42) (LitInt 1) == Just False@: Lit equality is +-- decidable +-- * @eqPmAltCon (DataCon A) (DataCon B) == Just False@: DataCon equality is +-- decidable +-- * @eqPmAltCon (LitOverInt 42) (LitOverInt 1) == Nothing@: OverLit equality +-- is undecidable +-- * @eqPmAltCon (PatSyn PA) (PatSyn PB) == Nothing@: PatSyn equality is +-- undecidable +-- * @eqPmAltCon (DataCon I#) (LitInt 1) == Nothing@: DataCon to Lit +-- comparisons are undecidable without reasoning about the wrapped @Int#@ +-- * @eqPmAltCon (LitOverInt 1) (LitOverInt 1) == Just True@: We assume +-- reflexivity for overloaded literals +-- * @eqPmAltCon (PatSyn PA) (PatSyn PA) == Just True@: We assume reflexivity +-- for Pattern Synonyms +eqPmAltCon :: PmAltCon -> PmAltCon -> PmEquality +eqPmAltCon (PmAltConLike cl1) (PmAltConLike cl2) = eqConLike cl1 cl2 +eqPmAltCon (PmAltLit l1) (PmAltLit l2) = eqPmLit l1 l2 +eqPmAltCon _ _ = PossiblyOverlap + +-- | Syntactic equality. instance Eq PmAltCon where - PmAltLit l1 == PmAltLit l2 = eqPmLit l1 l2 + a == b = eqPmAltCon a b == Equal -{- Note [Undecidable Equality for Overloaded Literals] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{- Note [Undecidable Equality for PmAltCons] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Equality on overloaded literals is undecidable in the general case. Consider the following example: @@ -96,25 +188,19 @@ the following example: f 1 = () -- Clause A f 2 = () -- Clause B -Clause B is redundant but to detect this, we should be able to solve the -constraint: False ~ (fromInteger 2 ~ fromInteger 1) which means that we -have to look through function `fromInteger`, whose implementation could +Clause B is redundant but to detect this, we must decide the constraint: +@fromInteger 2 ~ fromInteger 1@ which means that we +have to look through function @fromInteger@, whose implementation could be anything. This poses difficulties for: 1. The expressive power of the check. We cannot expect a reasonable implementation of pattern matching to detect - that fromInteger 2 ~ fromInteger 1 is True, unless we unfold function + that @fromInteger 2 ~ fromInteger 1@ is True, unless we unfold function fromInteger. This puts termination at risk and is undecidable in the general case. -2. Performance. - Having an unresolved constraint False ~ (fromInteger 2 ~ fromInteger 1) - lying around could become expensive really fast. Ticket #11161 illustrates - how heavy use of overloaded literals can generate plenty of those - constraints, effectively undermining the term oracle's performance. - -3. Error nessages/Warnings. - What should our message for `f` above be? A reasonable approach would be +2. Error messages/Warnings. + What should our message for @f@ above be? A reasonable approach would be to issue: Pattern matches are (potentially) redundant: @@ -122,8 +208,13 @@ be anything. This poses difficulties for: but seems to complex and confusing for the user. -We choose to treat overloaded literals that look different as different. The -impact of this is the following: +We choose to equate only obviously equal overloaded literals, in all other cases +we signal undecidability by returning Nothing from 'eqPmAltCons'. We do +better for non-overloaded literals, because we know their fromInteger/fromString +implementation is actually injective, allowing us to simplify the constraint +@fromInteger 1 ~ fromInteger 2@ to @1 ~ 2@, which is trivially unsatisfiable. + +The impact of this treatment of overloaded literals is the following: * Redundancy checking is rather conservative, since it cannot see that clause B above is redundant. @@ -136,120 +227,100 @@ impact of this is the following: * The warnings issued are simpler. - * We do not play on the safe side, strictly speaking. The assumption that - 1 /= 2 makes the redundancy check more conservative but at the same time - makes its dual (exhaustiveness check) unsafe. This we can live with, mainly - for two reasons: - 1. At the moment we do not use the results of the check during compilation - where this would be a disaster (could result in runtime errors even if - our function was deemed exhaustive). - 2. Pattern matcing on literals can never be considered exhaustive unless we - have a catch-all clause. Hence, this assumption affects mainly the - appearance of the warnings and is, in practice safe. +Similar reasoning applies to pattern synonyms: In contrast to data constructors, +which are generative, constraints like F a ~ G b for two different pattern +synonyms F and G aren't immediately unsatisfiable. We assume F a ~ F a, though. -} --- | A term constraint. @TVC x e@ encodes that @x@ is equal to @e@. -data TmVarCt = TVC !Id !PmExpr +literalToPmLit :: Type -> Literal -> Maybe PmLit +literalToPmLit ty l = PmLit ty <$> go l + where + go (LitChar c) = Just (PmLitChar c) + go (LitFloat r) = Just (PmLitRat r) + go (LitDouble r) = Just (PmLitRat r) + go (LitString s) = Just (PmLitString (mkFastStringByteString s)) + go (LitNumber _ i _) = Just (PmLitInt i) + go _ = Nothing + +negatePmLit :: PmLit -> Maybe PmLit +negatePmLit (PmLit ty v) = PmLit ty <$> go v + where + go (PmLitInt i) = Just (PmLitInt (-i)) + go (PmLitRat r) = Just (PmLitRat (-r)) + go (PmLitOverInt n i) = Just (PmLitOverInt (n+1) i) + go (PmLitOverRat n r) = Just (PmLitOverRat (n+1) r) + go _ = Nothing + +overloadPmLit :: Type -> PmLit -> Maybe PmLit +overloadPmLit ty (PmLit _ v) = PmLit ty <$> go v + where + go (PmLitInt i) = Just (PmLitOverInt 0 i) + go (PmLitRat r) = Just (PmLitOverRat 0 r) + go (PmLitString s) + | ty `eqType` stringTy = Just v + | otherwise = Just (PmLitOverString s) + go _ = Nothing -instance Outputable TmVarCt where - ppr (TVC x e) = ppr x <+> char '~' <+> ppr e +pmLitAsStringLit :: PmLit -> Maybe FastString +pmLitAsStringLit (PmLit _ (PmLitString s)) = Just s +pmLitAsStringLit _ = Nothing -- ---------------------------------------------------------------------------- -- ** Predicates on PmExpr --- | Check if an expression is lifted or not -isNotPmExprOther :: PmExpr -> Bool -isNotPmExprOther (PmExprOther _) = False -isNotPmExprOther _expr = True - -- ----------------------------------------------------------------------- -- ** Lift source expressions (HsExpr Id) to PmExpr -lhsExprToPmExpr :: LHsExpr GhcTc -> PmExpr -lhsExprToPmExpr (dL->L _ e) = hsExprToPmExpr e - -hsExprToPmExpr :: HsExpr GhcTc -> PmExpr - --- Translating HsVar to flexible meta variables in the unification problem is --- morally wrong, but it does the right thing for now. --- In contrast to the situation in pattern matches, HsVars in expression syntax --- are object language variables, most similar to rigid variables with an --- unknown solution. The correct way would be to handle them through PmExprOther --- and identify syntactically equal occurrences by the same rigid meta variable, --- but we can't compare the wrapped HsExpr for equality. Hence we are stuck with --- this hack. -hsExprToPmExpr (HsVar _ x) = PmExprVar (idName (unLoc x)) - --- Translating HsConLikeOut to a flexible meta variable is misleading. --- For an example why, consider `consAreRigid` in --- `testsuite/tests/pmcheck/should_compile/PmExprVars.hs`. --- hsExprToPmExpr (HsConLikeOut _ c) = PmExprVar (conLikeName c) - --- Desugar literal strings as a list of characters. For other literal values, --- keep it as it is. --- See `translatePat` in Check.hs (the `NPat` and `LitPat` case), and --- Note [Translate Overloaded Literal for Exhaustiveness Checking]. -hsExprToPmExpr (HsOverLit _ olit) - | OverLit (OverLitTc False ty) (HsIsString src s) _ <- olit, isStringTy ty - = stringExprToList src s - | otherwise = PmExprLit (PmOLit False olit) -hsExprToPmExpr (HsLit _ lit) - | HsString src s <- lit - = stringExprToList src s - | otherwise = PmExprLit (PmSLit lit) - -hsExprToPmExpr e@(NegApp _ (dL->L _ neg_expr) _) - | PmExprLit (PmOLit False olit) <- hsExprToPmExpr neg_expr - -- NB: DON'T simply @(NegApp (NegApp olit))@ as @x@. when extension - -- @RebindableSyntax@ enabled, (-(-x)) may not equals to x. - = PmExprLit (PmOLit True olit) - | otherwise = PmExprOther e - -hsExprToPmExpr (HsPar _ (dL->L _ e)) = hsExprToPmExpr e - -hsExprToPmExpr e@(ExplicitTuple _ ps boxity) - | all tupArgPresent ps = mkPmExprData tuple_con tuple_args - | otherwise = PmExprOther e - where - tuple_con = tupleDataCon boxity (length ps) - tuple_args = [ lhsExprToPmExpr e | (dL->L _ (Present _ e)) <- ps ] - -hsExprToPmExpr e@(ExplicitList _ mb_ol elems) - | Nothing <- mb_ol = foldr cons nil (map lhsExprToPmExpr elems) - | otherwise = PmExprOther e {- overloaded list: No PmExprApp -} - where - cons x xs = mkPmExprData consDataCon [x,xs] - nil = mkPmExprData nilDataCon [] - --- we want this but we would have to make everything monadic :/ --- ./compiler/deSugar/DsMonad.hs:397:dsLookupDataCon :: Name -> DsM DataCon --- --- hsExprToPmExpr (RecordCon c _ binds) = do --- con <- dsLookupDataCon (unLoc c) --- args <- mapM lhsExprToPmExpr (hsRecFieldsArgs binds) --- return (PmExprCon con args) -hsExprToPmExpr e@(RecordCon {}) = PmExprOther e - -hsExprToPmExpr (HsTick _ _ e) = lhsExprToPmExpr e -hsExprToPmExpr (HsBinTick _ _ _ e) = lhsExprToPmExpr e -hsExprToPmExpr (HsTickPragma _ _ _ _ e) = lhsExprToPmExpr e -hsExprToPmExpr (HsSCC _ _ _ e) = lhsExprToPmExpr e -hsExprToPmExpr (HsCoreAnn _ _ _ e) = lhsExprToPmExpr e -hsExprToPmExpr (ExprWithTySig _ e _) = lhsExprToPmExpr e -hsExprToPmExpr (HsWrap _ w e) - -- A dictionary application spoils e and we have no choice but to return an - -- PmExprOther. Same thing for other stuff that can't erased in the - -- compilation process. Otherwise this bites in - -- teststuite/tests/pmcheck/should_compile/PmExprVars.hs. - | isErasableHsWrapper w = hsExprToPmExpr e -hsExprToPmExpr e = PmExprOther e -- the rest are not handled by the oracle - -stringExprToList :: SourceText -> FastString -> PmExpr -stringExprToList src s = foldr cons nil (map charToPmExpr (unpackFS s)) +coreExprAsPmLit :: CoreExpr -> Maybe PmLit +-- coreExprAsPmLit e | pprTrace "coreExprAsPmLit" (ppr e) False = undefined +coreExprAsPmLit (Tick _t e) = coreExprAsPmLit e +coreExprAsPmLit (Lit l) = literalToPmLit (literalType l) l +coreExprAsPmLit e = case collectArgs e of + (Var x, [Lit l]) + | Just dc <- isDataConWorkId_maybe x + , dc `elem` [intDataCon, wordDataCon, charDataCon, floatDataCon, doubleDataCon] + -> literalToPmLit (exprType e) l + (Var x, [_ty, Lit n, Lit d]) + | Just dc <- isDataConWorkId_maybe x + , dataConName dc == ratioDataConName + -- HACK: just assume we have a literal double. This case only occurs for + -- overloaded lits anyway, so we immediately override type information + -> literalToPmLit (exprType e) (mkLitDouble (litValue n % litValue d)) + (Var x, args) + -- Take care of -XRebindableSyntax. The last argument should be the (only) + -- integer literal, otherwise we can't really do much about it. + | [Lit l] <- dropWhile (not . is_lit) args + -- getOccFS because of -XRebindableSyntax + , getOccFS (idName x) == getOccFS fromIntegerName + -> literalToPmLit (literalType l) l >>= overloadPmLit (exprType e) + (Var x, args) + -- Similar to fromInteger case + | [r] <- dropWhile (not . is_ratio) args + , getOccFS (idName x) == getOccFS fromRationalName + -> coreExprAsPmLit r >>= overloadPmLit (exprType e) + (Var x, [Type _ty, _dict, s]) + | idName x == fromStringName + -- NB: Calls coreExprAsPmLit and then overloadPmLit, so that we return PmLitOverStrings + -> coreExprAsPmLit s >>= overloadPmLit (exprType e) + -- These last two cases handle String literals + (Var x, [Type ty]) + | Just dc <- isDataConWorkId_maybe x + , dc == nilDataCon + , ty `eqType` charTy + -> literalToPmLit stringTy (mkLitString "") + (Var x, [Lit l]) + | idName x `elem` [unpackCStringName, unpackCStringUtf8Name] + -> literalToPmLit stringTy l + _ -> Nothing where - cons x xs = mkPmExprData consDataCon [x,xs] - nil = mkPmExprData nilDataCon [] - charToPmExpr c = PmExprLit (PmSLit (HsChar src c)) + is_lit Lit{} = True + is_lit _ = False + is_ratio (Type _) = False + is_ratio r + | Just (tc, _) <- splitTyConApp_maybe (exprType r) + = tyConName tc == ratioTyConName + | otherwise + = False {- %************************************************************************ @@ -259,22 +330,35 @@ stringExprToList src s = foldr cons nil (map charToPmExpr (unpackFS s)) %************************************************************************ -} -instance Outputable PmLit where - ppr (PmSLit l) = pmPprHsLit l - ppr (PmOLit neg l) = (if neg then char '-' else empty) <> ppr l +instance Outputable PmLitValue where + ppr (PmLitInt i) = ppr i + ppr (PmLitRat r) = ppr (double (fromRat r)) -- good enough + ppr (PmLitChar c) = pprHsChar c + ppr (PmLitString s) = pprHsString s + ppr (PmLitOverInt n i) = minuses n (ppr i) + ppr (PmLitOverRat n r) = minuses n (ppr (double (fromRat r))) + ppr (PmLitOverString s) = pprHsString s + +-- Take care of negated literals +minuses :: Int -> SDoc -> SDoc +minuses n sdoc = iterate (\sdoc -> parens (char '-' <> sdoc)) sdoc !! n -instance Outputable PmExpr where - ppr = go (0 :: Int) +instance Outputable PmLit where + ppr (PmLit ty v) = ppr v <> suffix where - go _ (PmExprLit l) = ppr l - go _ (PmExprVar v) = ppr v - go _ (PmExprOther e) = angleBrackets (ppr e) - go _ (PmExprCon (RealDataCon dc) args) - | isTupleDataCon dc = parens $ comma_sep $ map ppr args - | dc == consDataCon = brackets $ comma_sep $ map ppr (list_cells args) - where - comma_sep = fsep . punctuate comma - list_cells (hd:tl) = hd : list_cells tl - list_cells _ = [] - go prec (PmExprCon cl args) - = cparen (null args || prec > 0) (hcat (ppr cl:map (go 1) args)) + -- Some ad-hoc hackery for displaying proper lit suffixes based on type + tbl = [ (intPrimTy, primIntSuffix) + , (int64PrimTy, primInt64Suffix) + , (wordPrimTy, primWordSuffix) + , (word64PrimTy, primWord64Suffix) + , (charPrimTy, primCharSuffix) + , (floatPrimTy, primFloatSuffix) + , (doublePrimTy, primDoubleSuffix) ] + suffix = fromMaybe empty (snd <$> find (eqType ty . fst) tbl) + +instance Outputable PmAltCon where + ppr (PmAltConLike cl) = ppr cl + ppr (PmAltLit l) = ppr l + +instance Outputable PmEquality where + ppr = text . show diff --git a/compiler/deSugar/PmOracle.hs b/compiler/deSugar/PmOracle.hs new file mode 100644 index 0000000000..93c4d1d959 --- /dev/null +++ b/compiler/deSugar/PmOracle.hs @@ -0,0 +1,1872 @@ +{- +Authors: George Karachalias <george.karachalias@cs.kuleuven.be> + Sebastian Graf <sgraf1337@gmail.com> + Ryan Scott <ryan.gl.scott@gmail.com> +-} + +{-# LANGUAGE CPP, LambdaCase, TupleSections, PatternSynonyms, ViewPatterns, MultiWayIf #-} + +-- | The pattern match oracle. The main export of the module are the functions +-- 'addTmCt', 'refineToAltCon' and 'addRefutableAltCon' for adding +-- facts to the oracle, and 'provideEvidenceForEquation' to turn a 'Delta' into +-- a concrete evidence for an equation. +module PmOracle ( + + DsM, tracePm, mkPmId, + Delta, initDelta, canDiverge, lookupRefuts, lookupSolution, + lookupNumberOfRefinements, + + TmCt(..), + inhabitants, + addTypeEvidence, -- Add type equalities + addRefutableAltCon, -- Add a negative term equality + addTmCt, -- Add a positive term equality x ~ e + addVarCoreCt, -- Add a positive term equality x ~ core_expr + refineToAltCon, -- Add a positive refinement x ~ K _ _ + tmOracle, -- Add multiple positive term equalities + provideEvidenceForEquation, + ) where + +#include "HsVersions.h" + +import GhcPrelude + +import PmExpr + +import DynFlags +import Outputable +import ErrUtils +import Util +import Bag +import UniqDSet +import Unique +import Id +import VarEnv +import UniqDFM +import Var (EvVar) +import Name +import CoreSyn +import CoreOpt (exprIsConApp_maybe) +import CoreUtils (exprType) +import MkCore (mkListExpr, mkCharExpr) +import UniqSupply +import FastString +import SrcLoc +import ListSetOps (unionLists) +import Maybes +import ConLike +import DataCon +import PatSyn +import TyCon +import TysWiredIn +import TysPrim (tYPETyCon) +import TyCoRep +import Type +import TcSimplify (tcNormalise, tcCheckSatisfiability) +import Unify (tcMatchTy) +import TcRnTypes (pprEvVarWithType, completeMatchConLikes) +import Coercion +import MonadUtils hiding (foldlM) +import DsMonad hiding (foldlM) +import FamInst +import FamInstEnv + +import Control.Monad (zipWithM, guard, mzero) +import Control.Monad.Trans.Class (lift) +import Control.Monad.Trans.State.Strict +import Data.Bifunctor (second) +import Data.Foldable (foldlM) +import Data.List (find) +import Data.List.NonEmpty (NonEmpty (..)) +import qualified Data.List.NonEmpty as NonEmpty +import qualified Data.Semigroup as Semigroup + +-- Debugging Infrastructre + +tracePm :: String -> SDoc -> DsM () +tracePm herald doc = do + dflags <- getDynFlags + printer <- mkPrintUnqualifiedDs + liftIO $ dumpIfSet_dyn_printer printer dflags + Opt_D_dump_ec_trace (text herald $$ (nest 2 doc)) + +-- | 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) + +----------------------------------------------- +-- * Caching possible matches of a COMPLETE set + +type ConLikeSet = UniqDSet ConLike + +-- | A data type caching the results of 'completeMatchConLikes' with support for +-- deletion of contructors that were already matched on. +data PossibleMatches + = PM TyCon (NonEmpty ConLikeSet) + -- ^ Each ConLikeSet is a (subset of) the constructors in a COMPLETE pragma + -- 'NonEmpty' because the empty case would mean that the type has no COMPLETE + -- set at all, for which we have 'NoPM' + | NoPM + -- ^ No COMPLETE set for this type (yet). Think of overloaded literals. + +instance Outputable PossibleMatches where + ppr (PM _tc cs) = ppr (NonEmpty.toList cs) + ppr NoPM = text "<NoPM>" + +initIM :: Type -> DsM (Maybe PossibleMatches) +initIM ty = case splitTyConApp_maybe ty of + Nothing -> pure Nothing + 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 rdcs = maybeToList 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 + pure (PM tc . fmap mkUniqDSet <$> NonEmpty.nonEmpty (rdcs ++ pscs)) + +markMatched :: ConLike -> PossibleMatches -> PossibleMatches +markMatched con (PM tc ms) = PM tc (fmap (`delOneFromUniqDSet` con) ms) +markMatched _ NoPM = NoPM + +-- | Satisfiability decisions as a data type. The @proof@ can carry a witness +-- for satisfiability and might even be instantiated to 'Data.Void.Void' to +-- degenerate into a semi-decision predicate. +data Satisfiability proof + = Unsatisfiable + | PossiblySatisfiable + | Satisfiable !proof + +maybeSatisfiable :: Maybe a -> Satisfiability a +maybeSatisfiable (Just a) = Satisfiable a +maybeSatisfiable Nothing = Unsatisfiable + +-- | Tries to return one of the possible 'ConLike's from one of the COMPLETE +-- sets. If the 'PossibleMatches' was inhabited before (cf. 'ensureInhabited') +-- this 'ConLike' is evidence for that assurance. +getUnmatchedConstructor :: PossibleMatches -> Satisfiability ConLike +getUnmatchedConstructor NoPM = PossiblySatisfiable +getUnmatchedConstructor (PM _tc ms) + = maybeSatisfiable $ NonEmpty.head <$> traverse pick_one_conlike ms + where + pick_one_conlike cs = case uniqDSetToList cs of + [] -> Nothing + (cl:_) -> Just cl + +--------------------------------------------------- +-- * Instantiating constructors, types and evidence + +newEvVar :: Name -> Type -> EvVar +newEvVar name ty = mkLocalId name ty + +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) + +-- | Instantiate a 'ConLike' given its universal type arguments. Instantiates +-- existential and term binders with fresh variables of appropriate type. +-- Also returns instantiated evidence variables from the match and the types of +-- strict constructor fields. +mkOneConFull :: [Type] -> ConLike -> DsM ([Id], [EvVar], [Type], [TyVar]) +-- * 'con' K is a ConLike +-- - In the case of DataCons and most PatSynCons, these +-- are associated with a particular TyCon T +-- - But there are PatSynCons for this is not the case! See #11336, #17112 +-- +-- * 'arg_tys' tys are the types K's universally quantified type +-- variables should be instantiated to. +-- - For DataCons and most PatSyns these are the arguments of their TyCon +-- - For cases like in #11336, #17112, the univ_ts include those variables +-- from the view pattern, so tys will have to come from the type checker. +-- They can't easily be recovered from the result type. +-- +-- After instantiating the universal tyvars of K to tys we get +-- K @tys :: forall bs. Q => s1 .. sn -> T tys +-- Note that if K is a PatSynCon, depending on arg_tys, T might not necessarily +-- be a concrete TyCon. +-- +-- Suppose y1 is a strict field. Then we get +-- Results: [y1,..,yn] +-- Q +-- [s1] +-- [e1,..,en] +mkOneConFull arg_tys con = do + let (univ_tvs, ex_tvs, eq_spec, thetas, _req_theta , field_tys, _con_res_ty) + = conLikeFullSig con + -- pprTrace "mkOneConFull" (ppr con $$ ppr arg_tys $$ ppr univ_tvs $$ ppr _con_res_ty) (return ()) + -- Substitute universals for type arguments + let subst_univ = zipTvSubst univ_tvs arg_tys + -- Instantiate fresh existentials as arguments to the contructor + (subst, ex_tvs') <- cloneTyVarBndrs subst_univ ex_tvs <$> getUniqueSupplyM + let field_tys' = substTys subst field_tys + -- Instantiate fresh term variables (VAs) as arguments to the constructor + vars <- mapM mkPmId field_tys' + -- All constraints bound by the constructor (alpha-renamed), these are added + -- to the type oracle + let theta_cs = substTheta subst (eqSpecPreds eq_spec ++ thetas) + theta_ev_vars <- mapM (nameType "pm") theta_cs + -- Figure out the types of strict constructor fields + let arg_is_banged = map isBanged $ conLikeImplBangs con + strict_arg_tys = filterByList arg_is_banged field_tys' + return (vars, theta_ev_vars, strict_arg_tys, ex_tvs') + +equateTyVars :: [TyVar] -> [TyVar] -> DsM [EvVar] +equateTyVars ex_tvs1 ex_tvs2 + = ASSERT(ex_tvs1 `equalLength` ex_tvs2) + catMaybes <$> zipWithM mb_to_evvar ex_tvs1 ex_tvs2 + where + mb_to_evvar tv1 tv2 + | tv1 == tv2 = pure Nothing + | otherwise = Just <$> to_evvar tv1 tv2 + to_evvar tv1 tv2 = nameType "pmConCon" $ + mkPrimEqPred (mkTyVarTy tv1) (mkTyVarTy tv2) + +------------------------- +-- * Pattern match oracle + + +-- | Term and type constraints to accompany each value vector abstraction. +-- For efficiency, we store the term oracle state instead of the term +-- constraints. +data Delta = MkDelta { delta_ty_cs :: Bag EvVar -- Type oracle; things like a~Int + , delta_tm_cs :: TmState } -- Term oracle; things like x~Nothing + +-- | An initial delta that is always satisfiable +initDelta :: Delta +initDelta = MkDelta emptyBag initTmState + +instance Outputable Delta where + ppr delta = vcat [ + -- intentionally formatted this way enable the dev to comment in only + -- the info she needs + ppr (delta_tm_cs delta), + ppr (pprEvVarWithType <$> delta_ty_cs delta) + --ppr (delta_ty_cs delta) + ] + + +{- 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. +-} + +------------------------------------- +-- * Composable satisfiability checks + +-- | Given a 'Delta', check if it is compatible with new facts encoded in this +-- this check. If so, return 'Just' a potentially extended 'Delta'. Return +-- 'Nothing' if unsatisfiable. +-- +-- There are three essential SatisfiabilityChecks: +-- 1. 'tmIsSatisfiable', adding term oracle facts +-- 2. 'tyIsSatisfiable', adding type oracle facts +-- 3. 'tysAreNonVoid', checks if the given types have an inhabitant +-- Functions like 'pmIsSatisfiable', 'nonVoid' and 'testInhabited' plug these +-- together as they see fit. +newtype SatisfiabilityCheck = SC (Delta -> DsM (Maybe Delta)) + +-- | Check the given 'Delta' for satisfiability by the the given +-- 'SatisfiabilityCheck'. Return 'Just' a new, potentially extended, 'Delta' if +-- successful, and 'Nothing' otherwise. +runSatisfiabilityCheck :: Delta -> SatisfiabilityCheck -> DsM (Maybe Delta) +runSatisfiabilityCheck delta (SC chk) = chk delta + +-- | Allowing easy composition of 'SatisfiabilityCheck's. +instance Semigroup SatisfiabilityCheck where + -- This is @a >=> b@ from MaybeT DsM + SC a <> SC b = SC c + where + c delta = a delta >>= \case + Nothing -> pure Nothing + Just delta' -> b delta' + +instance Monoid SatisfiabilityCheck where + -- We only need this because of mconcat (which we use in place of sconcat, + -- which requires NonEmpty lists as argument, making all call sites ugly) + mempty = SC (pure . Just) + +------------------------------- +-- * Oracle transition function + +-- | Given a conlike's term constraints, type constraints, and strict argument +-- types, check if they are satisfiable. +-- (In other words, this is the ⊢_Sat oracle judgment from the GADTs Meet +-- Their Match paper.) +-- +-- Taking strict argument types into account is something which was not +-- discussed in GADTs Meet Their Match. For an explanation of what role they +-- serve, see @Note [Strict argument type constraints]@. +pmIsSatisfiable + :: Delta -- ^ The ambient term and type constraints + -- (known to be satisfiable). + -> Bag TmCt -- ^ The new term constraints. + -> Bag EvVar -- ^ The new type constraints. + -> [Type] -- ^ The strict argument types. + -> DsM (Maybe Delta) + -- ^ @'Just' delta@ if the constraints (@delta@) are + -- satisfiable, and each strict argument type is inhabitable. + -- 'Nothing' otherwise. +pmIsSatisfiable amb_cs new_tm_cs new_ty_cs strict_arg_tys = + -- The order is important here! Check the new type constraints before we check + -- whether strict argument types are inhabited given those constraints. + runSatisfiabilityCheck amb_cs $ mconcat + [ tyIsSatisfiable True new_ty_cs + , tmIsSatisfiable new_tm_cs + , tysAreNonVoid initRecTc strict_arg_tys + ] + +----------------------- +-- * Type normalisation + +-- | The return value of 'pmTopNormaliseType' +data TopNormaliseTypeResult + = NoChange Type + -- ^ 'tcNormalise' failed to simplify the type and 'topNormaliseTypeX' was + -- unable to reduce the outermost type application, so the type came out + -- unchanged. + | NormalisedByConstraints Type + -- ^ 'tcNormalise' was able to simplify the type with some local constraint + -- from the type oracle, but 'topNormaliseTypeX' couldn't identify a type + -- redex. + | HadRedexes Type [(Type, DataCon)] Type + -- ^ 'tcNormalise' may or may not been able to simplify the type, but + -- 'topNormaliseTypeX' made progress either way and got rid of at least one + -- outermost type or data family redex or newtype. + -- The first field is the last type that was reduced solely through type + -- family applications (possibly just the 'tcNormalise'd type). This is the + -- one that is equal (in source Haskell) to the initial type. + -- The third field is the type that we get when also looking through data + -- family applications and newtypes. This would be the representation type in + -- Core (modulo casts). + -- The second field is the list of Newtype 'DataCon's that we looked through + -- in the chain of reduction steps between the Source type and the Core type. + -- We also keep the type of the DataCon application, so that we don't have to + -- reconstruct it in inhabitationCandidates.build_newtype. + +-- | Just give me the potentially normalised source type, unchanged or not! +normalisedSourceType :: TopNormaliseTypeResult -> Type +normalisedSourceType (NoChange ty) = ty +normalisedSourceType (NormalisedByConstraints ty) = ty +normalisedSourceType (HadRedexes ty _ _) = ty + +instance Outputable TopNormaliseTypeResult where + ppr (NoChange ty) = text "NoChange" <+> ppr ty + ppr (NormalisedByConstraints ty) = text "NormalisedByConstraints" <+> ppr ty + ppr (HadRedexes src_ty ds core_ty) = text "HadRedexes" <+> braces fields + where + fields = fsep (punctuate comma [ text "src_ty =" <+> ppr src_ty + , text "newtype_dcs =" <+> ppr ds + , text "core_ty =" <+> ppr core_ty ]) + +pmTopNormaliseType :: Bag EvVar -> Type -> DsM TopNormaliseTypeResult +-- ^ Get rid of *outermost* (or toplevel) +-- * type function redex +-- * data family redex +-- * newtypes +-- +-- Behaves 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. +-- It also initially 'tcNormalise's the type with the bag of local constraints. +-- +-- See 'TopNormaliseTypeResult' for the meaning of the return value. +-- +-- 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 ty_cs typ + = do env <- dsGetFamInstEnvs + -- 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]. + (_, mb_typ') <- initTcDsForSolver $ tcNormalise ty_cs typ + -- If tcNormalise didn't manage to simplify the type, continue anyway. + -- We might be able to reduce type applications nonetheless! + let typ' = fromMaybe typ mb_typ' + -- Now we look with topNormaliseTypeX through type and data family + -- applications and newtypes, which tcNormalise does not do. + -- See also 'TopNormaliseTypeResult'. + pure $ case topNormaliseTypeX (stepper env) comb typ' of + Nothing + | Nothing <- mb_typ' -> NoChange typ + | otherwise -> NormalisedByConstraints typ' + Just ((ty_f,tm_f), ty) -> HadRedexes src_ty newtype_dcs core_ty + where + src_ty = eq_src_ty ty (typ' : ty_f [ty]) + newtype_dcs = tm_f [] + core_ty = 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 env = newTypeStepper `composeSteppers` tyFamStepper env + + -- 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],[(Type, DataCon)] -> [(Type, DataCon)]) + newTypeStepper rec_nts tc tys + | Just (ty', _co) <- instNewTyCon_maybe tc tys + , let orig_ty = TyConApp tc tys + = case checkRecTc rec_nts tc of + Just rec_nts' -> let tyf = (orig_ty:) + tmf = ((orig_ty, tyConSingleDataCon tc):) + in NS_Step rec_nts' ty' (tyf, tmf) + Nothing -> NS_Abort + | otherwise + = NS_Done + + tyFamStepper :: FamInstEnvs -> NormaliseStepper ([Type] -> [Type], a -> a) + tyFamStepper env 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 + +-- | 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 + +{- 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 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 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 newtype constructors "skipped", every time we normalise + a newtype to its core representation, we keep track of the source data + constructor and the type we unwrap. + (c) core_ty is the rewritten type. That is, + pmTopNormaliseType 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 env ty_cs (F Int) results in + + Just (G2, [(G2,MkG2),(G1,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, using the constraint solver to solve for any local +equalities (such as i ~ Int) that may be in scope. +-} + +---------------- +-- * Type oracle + +-- | Check whether a set of type constraints is satisfiable. +tyOracle :: Bag EvVar -> DsM Bool +tyOracle evs + = do { ((_warns, errs), res) <- initTcDsForSolver $ tcCheckSatisfiability evs + ; case res of + Just sat -> return sat + Nothing -> pprPanic "tyOracle" (vcat $ pprErrMsgBagWithLoc errs) } + +-- | A 'SatisfiabilityCheck' based on new type-level constraints. +-- Returns a new 'Delta' if the new constraints are compatible with existing +-- ones. Doesn't bother calling out to the type oracle if the bag of new type +-- constraints was empty. Will only recheck 'PossibleMatches' in the term oracle +-- for emptiness if the first argument is 'True'. +tyIsSatisfiable :: Bool -> Bag EvVar -> SatisfiabilityCheck +tyIsSatisfiable recheck_complete_sets new_ty_cs = SC $ \delta -> do + tracePm "tyIsSatisfiable" (ppr (fmap pprEvVarWithType new_ty_cs)) + let ty_cs = new_ty_cs `unionBags` delta_ty_cs delta + let delta' = delta{ delta_ty_cs = ty_cs } + if isEmptyBag new_ty_cs + then pure (Just delta) + else tyOracle ty_cs >>= \case + False -> pure Nothing + True + | recheck_complete_sets -> ensureAllPossibleMatchesInhabited delta' + | otherwise -> pure (Just delta') + + +{- ********************************************************************* +* * + SharedIdEnv + DIdEnv with sharing +* * +********************************************************************* -} + +-- | Either @Indirect x@, meaning the value is represented by that of @x@, or +-- an @Entry@ containing containing the actual value it represents. +data Shared a + = Indirect Id + | Entry a + +-- | A 'DIdEnv' in which entries can be shared by multiple 'Id's. +-- Merge equivalence classes of two Ids by 'setIndirectSDIE' and set the entry +-- of an Id with 'setEntrySDIE'. +newtype SharedDIdEnv a + = SDIE { unSDIE :: DIdEnv (Shared a) } + +emptySDIE :: SharedDIdEnv a +emptySDIE = SDIE emptyDVarEnv + +lookupReprAndEntrySDIE :: SharedDIdEnv a -> Id -> (Id, Maybe a) +lookupReprAndEntrySDIE sdie@(SDIE env) x = case lookupDVarEnv env x of + Nothing -> (x, Nothing) + Just (Indirect y) -> lookupReprAndEntrySDIE sdie y + Just (Entry a) -> (x, Just a) + +-- | @lookupSDIE env x@ looks up an entry for @x@, looking through all +-- 'Indirect's until it finds a shared 'Entry'. +lookupSDIE :: SharedDIdEnv a -> Id -> Maybe a +lookupSDIE sdie x = snd (lookupReprAndEntrySDIE sdie x) + +-- | Check if two variables are part of the same equivalence class. +sameRepresentative :: SharedDIdEnv a -> Id -> Id -> Bool +sameRepresentative sdie x y = + fst (lookupReprAndEntrySDIE sdie x) == fst (lookupReprAndEntrySDIE sdie y) + +-- | @setIndirectSDIE env x y@ sets @x@'s 'Entry' to @Indirect y@, thereby +-- merging @x@'s equivalence class into @y@'s. This will discard all info on +-- @x@! +setIndirectSDIE :: SharedDIdEnv a -> Id -> Id -> SharedDIdEnv a +setIndirectSDIE sdie@(SDIE env) x y = + SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Indirect y) + +-- | @setEntrySDIE env x a@ sets the 'Entry' @x@ is associated with to @a@, +-- thereby modifying its whole equivalence class. +setEntrySDIE :: SharedDIdEnv a -> Id -> a -> SharedDIdEnv a +setEntrySDIE sdie@(SDIE env) x a = + SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Entry a) + +traverseSharedIdEnv :: Applicative f => (a -> f b) -> SharedDIdEnv a -> f (SharedDIdEnv b) +traverseSharedIdEnv f = fmap (SDIE . listToUDFM) . traverse g . udfmToList . unSDIE + where + g (u, Indirect y) = pure (u,Indirect y) + g (u, Entry a) = (u,) . Entry <$> f a + +instance Outputable a => Outputable (Shared a) where + ppr (Indirect x) = ppr x + ppr (Entry a) = ppr a + +instance Outputable a => Outputable (SharedDIdEnv a) where + ppr (SDIE env) = ppr env + + +{- ********************************************************************* +* * + TmState + What we know about terms +* * +********************************************************************* -} + +-- | The term oracle state. Stores 'VarInfo' for encountered 'Id's. These +-- entries are possibly shared when we figure out that two variables must be +-- equal, thus represent the same set of values. +-- +-- See Note [TmState invariants]. +newtype TmState = TS (SharedDIdEnv VarInfo) + -- Deterministic so that we generate deterministic error messages + +-- | Information about an 'Id'. Stores positive ('vi_pos') facts, like @x ~ Just 42@, +-- and negative ('vi_neg') facts, like "x is not (:)". +-- Also caches the type ('vi_ty'), the 'PossibleMatches' of a COMPLETE set +-- ('vi_cache') and the number of times each variable was refined +-- ('vi_n_refines'). +-- +-- Subject to Note [The Pos/Neg invariant]. +data VarInfo + = VI + { vi_ty :: !Type + -- ^ The type of the variable. Important for rejecting possible GADT + -- constructors or incompatible pattern synonyms (@Just42 :: Maybe Int@). + + , vi_pos :: [(PmAltCon, [Id])] + -- ^ Positive info: 'PmExprCon's it is (i.e. @x ~ [Just y, PatSyn z]@), all + -- at the same time (i.e. conjunctive). We need a list because of nested + -- pattern matches involving pattern synonym + -- case x of { Just y -> case x of PatSyn z -> ... } + -- However, no more than one RealDataCon in the list, otherwise contradiction + -- because of generativity. + + , vi_neg :: ![PmAltCon] + -- ^ Negative info: A list of 'PmAltCon's that it cannot match. + -- Example, assuming + -- + -- @ + -- data T = Leaf Int | Branch T T | Node Int T + -- @ + -- + -- then @x /~ [Leaf, Node]@ means that @x@ cannot match a @Leaf@ or @Node@, + -- and hence can only match @Branch@. Is orthogonal to anything from 'vi_pos', + -- in the sense that 'eqPmAltCon' returns @PossiblyOverlap@ for any pairing + -- between 'vi_pos' and 'vi_neg'. + + -- See Note [Why record both positive and negative info?] + + , vi_cache :: !PossibleMatches + -- ^ A cache of the associated COMPLETE sets. At any time a superset of + -- possible constructors of each COMPLETE set. So, if it's not in here, we + -- can't possibly match on it. Complementary to 'vi_neg'. We still need it + -- to recognise completion of a COMPLETE set efficiently for large enums. + + , vi_n_refines :: !Int + -- ^ Purely for Check performance reasons. The number of times this + -- representative was refined ('refineToAltCon') in the Check's ConVar split. + -- Sadly, we can't store this info in the Check module, as it's tightly coupled + -- to the particular 'Delta' and also is per *representative*, not per + -- syntactic variable. Note that this number does not always correspond to the + -- length of solutions: 'addVarConCt' might add a solution without + -- incurring the potential exponential blowup by ConVar. + } + +{- Note [The Pos/Neg invariant] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Invariant applying to each VarInfo: Whenever we have @(C, [y,z])@ in 'vi_pos', +any entry in 'vi_neg' must be incomparable to C (return Nothing) according to +'eqPmAltCons'. Those entries that are comparable either lead to a refutation +or are redudant. Examples: +* @x ~ Just y@, @x /~ [Just]@. 'eqPmAltCon' returns @Equal@, so refute. +* @x ~ Nothing@, @x /~ [Just]@. 'eqPmAltCon' returns @Disjoint@, so negative + info is redundant and should be discarded. +* @x ~ I# y@, @x /~ [4,2]@. 'eqPmAltCon' returns @PossiblyOverlap@, so orthogal. + We keep this info in order to be able to refute a redundant match on i.e. 4 + later on. + +This carries over to pattern synonyms and overloaded literals. Say, we have + pattern Just42 = Just 42 + case Just42 of x + Nothing -> () + Just _ -> () +Even though we had a solution for the value abstraction called x here in form +of a PatSynCon (Just42,[]), this solution is incomparable to both Nothing and +Just. Hence we retain the info in vi_neg, which eventually allows us to detect +the complete pattern match. + +The Pos/Neg invariant extends to vi_cache, which stores essentially positive +information. We make sure that vi_neg and vi_cache never overlap. This isn't +strictly necessary since vi_cache is just a cache, so doesn't need to be +accurate: Every suggestion of a possible ConLike from vi_cache might be +refutable by the type oracle anyway. But it helps to maintain sanity while +debugging traces. + +Note [Why record both positive and negative info?] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You might think that knowing positive info (like x ~ Just y) would render +negative info irrelevant, but not so because of pattern synonyms. E.g we might +know that x cannot match (Foo 4), where pattern Foo p = Just p + +Also overloaded literals themselves behave like pattern synonyms. E.g if +postively we know that (x ~ I# y), we might also negatively want to record that +x does not match 45 f 45 = e2 f (I# 22#) = e3 f 45 = e4 -- +Overlapped + +Note [TmState invariants] +~~~~~~~~~~~~~~~~~~~~~~~~~ +The term oracle state is never obviously (i.e., without consulting the type +oracle) contradictory. This implies a few invariants: +* Whenever vi_pos overlaps with vi_neg according to 'eqPmAltCon', we refute. + This is implied by the Note [Pos/Neg invariant]. +* Whenever vi_neg subsumes a COMPLETE set, we refute. We consult vi_cache to + detect this, but we could just compare whole COMPLETE sets to vi_neg every + time, if it weren't for performance. + +Maintaining these invariants in 'addVarVarCt' (the core of the term oracle) and +'addRefutableAltCon' is subtle. +* Merging VarInfos. Example: Add the fact @x ~ y@ (see 'equate'). + - (COMPLETE) If we had @x /~ True@ and @y /~ False@, then we get + @x /~ [True,False]@. This is vacuous by matter of comparing to the vanilla + COMPLETE set, so should refute. + - (Pos/Neg) If we had @x /~ True@ and @y ~ True@, we have to refute. +* Adding positive information. Example: Add the fact @x ~ K ys@ (see 'addVarConCt') + - (Neg) If we had @x /~ K@, refute. + - (Pos) If we had @x ~ K2@, and that contradicts the new solution according to + 'eqPmAltCon' (ex. K2 is [] and K is (:)), then refute. + - (Refine) If we had @x /~ K zs@, unify each y with each z in turn. +* Adding negative information. Example: Add the fact @x /~ Nothing@ (see 'addRefutableAltCon') + - (Refut) If we have @x ~ K ys@, refute. + - (Redundant) If we have @x ~ K2@ and @eqPmAltCon K K2 == Disjoint@ + (ex. Just and Nothing), the info is redundant and can be + discarded. + - (COMPLETE) If K=Nothing and we had @x /~ Just@, then we get + @x /~ [Just,Nothing]@. This is vacuous by matter of comparing to the vanilla + COMPLETE set, so should refute. + +Note that merging VarInfo in equate can be done by calling out to 'addVarConCt' and +'addRefutableAltCon' for each of the facts individually. + +Note [Representation of Strings in TmState] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Instead of treating regular String literals as a PmLits, we treat it as a list +of characters in the oracle for better overlap reasoning. The following example +shows why: + + f :: String -> () + f ('f':_) = () + f "foo" = () + f _ = () + +The second case is redundant, and we like to warn about it. Therefore either +the oracle will have to do some smart conversion between the list and literal +representation or treat is as the list it really is at runtime. + +The "smart conversion" has the advantage of leveraging the more compact literal +representation wherever possible, but is really nasty to get right with negative +equalities: Just think of how to encode @x /= "foo"@. +The "list" option is far simpler, but incurs some overhead in representation and +warning messages (which can be alleviated by someone with enough dedication). +-} + +-- | Not user-facing. +instance Outputable TmState where + ppr (TS state) = ppr state + +-- | Not user-facing. +instance Outputable VarInfo where + ppr (VI ty pos neg cache n) + = braces (hcat (punctuate comma [ppr ty, ppr pos, ppr neg, ppr cache, ppr n])) + +-- | Initial state of the oracle. +initTmState :: TmState +initTmState = TS emptySDIE + +-- | A 'SatisfiabilityCheck' based on new term-level constraints. +-- Returns a new 'Delta' if the new constraints are compatible with existing +-- ones. +tmIsSatisfiable :: Bag TmCt -> SatisfiabilityCheck +tmIsSatisfiable new_tm_cs = SC $ \delta -> tmOracle delta new_tm_cs + +-- | External interface to the term oracle. +tmOracle :: Foldable f => Delta -> f TmCt -> DsM (Maybe Delta) +tmOracle delta = runMaybeT . foldlM go delta + where + go delta ct = MaybeT (addTmCt delta ct) + +----------------------- +-- * Looking up VarInfo + +emptyVarInfo :: Id -> VarInfo +emptyVarInfo x = VI (idType x) [] [] NoPM 0 + +lookupVarInfo :: TmState -> Id -> VarInfo +-- (lookupVarInfo tms x) tells what we know about 'x' +lookupVarInfo (TS env) x = fromMaybe (emptyVarInfo x) (lookupSDIE env x) + +initPossibleMatches :: Bag EvVar -> VarInfo -> DsM VarInfo +initPossibleMatches ty_cs vi@VI{ vi_ty = ty, vi_cache = NoPM } = do + -- New evidence might lead to refined info on ty, in turn leading to discovery + -- of a COMPLETE set. + res <- pmTopNormaliseType ty_cs ty + let ty' = normalisedSourceType res + mb_pm <- initIM ty' + -- tracePm "initPossibleMatches" (ppr vi $$ ppr ty' $$ ppr res $$ ppr mb_pm) + case mb_pm of + Nothing -> pure vi + Just pm -> pure vi{ vi_ty = ty', vi_cache = pm } +initPossibleMatches _ vi = pure vi + +-- | @initLookupVarInfo ts x@ looks up the 'VarInfo' for @x@ in @ts@ and tries +-- to initialise the 'vi_cache' component if it was 'NoPM' through +-- 'initPossibleMatches'. +initLookupVarInfo :: Delta -> Id -> DsM VarInfo +initLookupVarInfo MkDelta{ delta_tm_cs = ts, delta_ty_cs = ty_cs } x + = initPossibleMatches ty_cs (lookupVarInfo ts x) + +------------------------------------------------ +-- * Exported utility functions querying 'Delta' + +-- | Check whether a constraint (x ~ BOT) can succeed, +-- given the resulting state of the term oracle. +canDiverge :: Delta -> Id -> Bool +canDiverge MkDelta{ delta_tm_cs = ts } x + -- If the variable seems not evaluated, there is a possibility for + -- constraint x ~ BOT to be satisfiable. That's the case when we haven't found + -- a solution (i.e. some equivalent literal or constructor) for it yet. + -- Even if we don't have a solution yet, it might be involved in a negative + -- constraint, in which case we must already have evaluated it earlier. + | VI _ [] [] _ _ <- lookupVarInfo ts x + = True + -- Variable x is already in WHNF or we know some refutable shape, so the + -- constraint is non-satisfiable + | otherwise = False + +lookupRefuts :: Uniquable k => Delta -> k -> [PmAltCon] +-- Unfortunately we need the extra bit of polymorphism and the unfortunate +-- duplication of lookupVarInfo here. +lookupRefuts MkDelta{ delta_tm_cs = ts@(TS (SDIE env)) } k = + case lookupUDFM env k of + Nothing -> [] + Just (Indirect y) -> vi_neg (lookupVarInfo ts y) + Just (Entry vi) -> vi_neg vi + +isDataConSolution :: (PmAltCon, [Id]) -> Bool +isDataConSolution (PmAltConLike (RealDataCon _), _) = True +isDataConSolution _ = False + +-- @lookupSolution delta x@ picks a single solution ('vi_pos') of @x@ from +-- possibly many, preferring 'RealDataCon' solutions whenever possible. +lookupSolution :: Delta -> Id -> Maybe (PmAltCon, [Id]) +lookupSolution delta x = case vi_pos (lookupVarInfo (delta_tm_cs delta) x) of + [] -> Nothing + pos + | Just sol <- find isDataConSolution pos -> Just sol + | otherwise -> Just (head pos) + +-- | @lookupNumberOfRefinements delta x@ Looks up how many times we have refined +-- ('refineToAltCon') @x@ for some 'PmAltCon' to arrive at @delta@. This number +-- is always less or equal to @length (lookupSolution delta x)@! +lookupNumberOfRefinements :: Delta -> Id -> Int +lookupNumberOfRefinements delta x + = vi_n_refines (lookupVarInfo (delta_tm_cs delta) x) + +------------------------------- +-- * Adding facts to the oracle + +-- | A term constraint. Either equates two variables or a variable with a +-- 'PmAltCon' application. +data TmCt + = TmVarVar !Id !Id + | TmVarCon !Id !PmAltCon ![Id] + +instance Outputable TmCt where + ppr (TmVarVar x y) = ppr x <+> char '~' <+> ppr y + ppr (TmVarCon x con args) = ppr x <+> char '~' <+> hsep (ppr con : map ppr args) + +-- | Add type equalities to 'Delta'. +addTypeEvidence :: Delta -> Bag EvVar -> DsM (Maybe Delta) +addTypeEvidence delta dicts + = runSatisfiabilityCheck delta (tyIsSatisfiable True dicts) + +-- | Tries to equate two representatives in 'Delta'. +-- See Note [TmState invariants]. +addTmCt :: Delta -> TmCt -> DsM (Maybe Delta) +addTmCt delta ct = runMaybeT $ case ct of + TmVarVar x y -> addVarVarCt delta (x, y) + TmVarCon x con args -> addVarConCt delta x con args + +-- | Record that a particular 'Id' can't take the shape of a 'PmAltCon' in the +-- 'Delta' and return @Nothing@ if that leads to a contradiction. +-- See Note [TmState invariants]. +addRefutableAltCon :: Delta -> Id -> PmAltCon -> DsM (Maybe Delta) +addRefutableAltCon delta@MkDelta{ delta_tm_cs = TS env } x nalt = runMaybeT $ do + vi@(VI _ pos neg pm _) <- lift (initLookupVarInfo delta x) + -- 1. Bail out quickly when nalt contradicts a solution + let contradicts nalt (cl, _args) = eqPmAltCon cl nalt == Equal + guard (not (any (contradicts nalt) pos)) + -- 2. Only record the new fact when it's not already implied by one of the + -- solutions + let implies nalt (cl, _args) = eqPmAltCon cl nalt == Disjoint + let neg' + | any (implies nalt) pos = neg + -- See Note [Completeness checking with required Thetas] + | hasRequiredTheta nalt = neg + | otherwise = unionLists neg [nalt] + let vi_ext = vi{ vi_neg = neg' } + -- 3. Make sure there's at least one other possible constructor + vi' <- case nalt of + PmAltConLike cl + -> MaybeT (ensureInhabited delta vi_ext{ vi_cache = markMatched cl pm }) + _ -> pure vi_ext + pure delta{ delta_tm_cs = TS (setEntrySDIE env x vi') } + +hasRequiredTheta :: PmAltCon -> Bool +hasRequiredTheta (PmAltConLike cl) = notNull req_theta + where + (_,_,_,_,req_theta,_,_) = conLikeFullSig cl +hasRequiredTheta _ = False + +{- Note [Completeness checking with required Thetas] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider the situation in #11224 + + import Text.Read (readMaybe) + pattern PRead :: Read a => () => a -> String + pattern PRead x <- (readMaybe -> Just x) + f :: String -> Int + f (PRead x) = x + f (PRead xs) = length xs + f _ = 0 + +Is the first match exhaustive on the PRead synonym? Should the second line thus +deemed redundant? The answer is, of course, No! The required theta is like a +hidden parameter which must be supplied at the pattern match site, so PRead +is much more like a view pattern (where behavior depends on the particular value +passed in). +The simple solution here is to forget in 'addRefutableAltCon' that we matched +on synonyms with a required Theta like @PRead@, so that subsequent matches on +the same constructor are never flagged as redundant. The consequence is that +we no longer detect the actually redundant match in + + g :: String -> Int + g (PRead x) = x + g (PRead y) = y -- redundant! + g _ = 0 + +But that's a small price to pay, compared to the proper solution here involving +storing required arguments along with the PmAltConLike in 'vi_neg'. +-} + +-- | Guess the universal argument types of a ConLike from an instantiation of +-- its result type. Rather easy for DataCons, but not so much for PatSynCons. +-- See Note [Pattern synonym result type] in PatSyn.hs. +guessConLikeUnivTyArgsFromResTy :: FamInstEnvs -> Type -> ConLike -> Maybe [Type] +guessConLikeUnivTyArgsFromResTy env res_ty (RealDataCon _) = do + (tc, tc_args) <- splitTyConApp_maybe res_ty + -- Consider data families: In case of a DataCon, we need to translate to + -- the representation TyCon. For PatSyns, they are relative to the data + -- family TyCon, so we don't need to translate them. + let (_, tc_args', _) = tcLookupDataFamInst env tc tc_args + Just tc_args' +guessConLikeUnivTyArgsFromResTy _ res_ty (PatSynCon ps) = do + -- We were successful if we managed to instantiate *every* univ_tv of con. + -- This is difficult and bound to fail in some cases, see + -- Note [Pattern synonym result type] in PatSyn.hs. So we just try our best + -- here and be sure to return an instantiation when we can substitute every + -- universally quantified type variable. + -- We *could* instantiate all the other univ_tvs just to fresh variables, I + -- suppose, but that means we get weird field types for which we don't know + -- anything. So we prefer to keep it simple here. + let (univ_tvs,_,_,_,_,con_res_ty) = patSynSig ps + subst <- tcMatchTy con_res_ty res_ty + traverse (lookupTyVar subst) univ_tvs + +ensureInhabited :: Delta -> VarInfo -> DsM (Maybe VarInfo) + -- Returns (Just vi) guarantees that at least one member + -- of each ConLike in the COMPLETE set satisfies the oracle + -- + -- Internally uses and updates the ConLikeSets in vi_cache. + -- + -- NB: Does /not/ filter each ConLikeSet with the oracle; members may + -- remain that do not statisfy it. This lazy approach just + -- avoids doing unnecessary work. +ensureInhabited delta vi = fmap (set_cache vi) <$> test (vi_cache vi) -- This would be much less tedious with lenses + where + set_cache vi cache = vi { vi_cache = cache } + + test NoPM = pure (Just NoPM) + test (PM tc ms) = runMaybeT (PM tc <$> traverse one_set ms) + + one_set cs = find_one_inh cs (uniqDSetToList cs) + + find_one_inh :: ConLikeSet -> [ConLike] -> MaybeT DsM ConLikeSet + -- (find_one_inh cs cls) iterates over cls, deleting from cs + -- any uninhabited elements of cls. Stop (returning Just cs) + -- when you see an inhabited element; return Nothing if all + -- are uninhabited + find_one_inh _ [] = mzero + find_one_inh cs (con:cons) = lift (inh_test con) >>= \case + True -> pure cs + False -> find_one_inh (delOneFromUniqDSet cs con) cons + + inh_test :: ConLike -> DsM Bool + -- @inh_test K@ Returns False if a non-bottom value @v::ty@ cannot possibly + -- be of form @K _ _ _@. Returning True is always sound. + -- + -- It's like 'DataCon.dataConCannotMatch', but more clever because it takes + -- the facts in Delta into account. + inh_test con = do + env <- dsGetFamInstEnvs + case guessConLikeUnivTyArgsFromResTy env (vi_ty vi) con of + Nothing -> pure True -- be conservative about this + Just arg_tys -> do + (_vars, ev_vars, strict_arg_tys, _ex_tyvars) <- mkOneConFull arg_tys con + -- No need to run the term oracle compared to pmIsSatisfiable + fmap isJust <$> runSatisfiabilityCheck delta $ mconcat + -- Important to pass False to tyIsSatisfiable here, so that we won't + -- recursively call ensureAllPossibleMatchesInhabited, leading to an + -- endless recursion. + [ tyIsSatisfiable False (listToBag ev_vars) + , tysAreNonVoid initRecTc strict_arg_tys + ] + +-- | Checks if every 'VarInfo' in the term oracle has still an inhabited +-- 'vi_cache', considering the current type information in 'Delta'. +-- This check is necessary after having matched on a GADT con to weed out +-- impossible matches. +ensureAllPossibleMatchesInhabited :: Delta -> DsM (Maybe Delta) +ensureAllPossibleMatchesInhabited delta@MkDelta{ delta_tm_cs = TS env } + = runMaybeT (set_tm_cs_env delta <$> traverseSharedIdEnv go env) + where + set_tm_cs_env delta env = delta{ delta_tm_cs = TS env } + go vi = MaybeT (ensureInhabited delta vi) + +-- | @refineToAltCon delta x con arg_tys ex_tyvars@ instantiates @con@ at +-- @arg_tys@ with fresh variables (equating existentials to @ex_tyvars@). +-- It adds a new term equality equating @x@ is to the resulting 'PmExprCon' and +-- new type equalities arising from GADT matches. +-- If successful, returns the new @delta@ and the fresh term variables, or +-- @Nothing@ otherwise. +refineToAltCon :: Delta -> Id -> PmAltCon -> [Type] -> [TyVar] -> DsM (Maybe (Delta, [Id])) +refineToAltCon delta x l@PmAltLit{} _arg_tys _ex_tvs1 = runMaybeT $ do + delta' <- addVarConCt delta x l [] + pure (markRefined delta' x, []) +refineToAltCon delta x alt@(PmAltConLike con) arg_tys ex_tvs1 = do + -- The plan for ConLikes: + -- Suppose K :: forall a b y z. (y,b) -> z -> T a b + -- where the y,z are the existentials + -- @refineToAltCon delta x K [ex1, ex2]@ extends delta with the + -- positive information x :-> K y' z' p q, for some fresh y', z', p, q. + -- This is done by mkOneConFull. + -- We return the fresh [p,q] args, and bind the existentials [y',z'] to + -- [ex1, ex2]. + -- Return Nothing if such a match is contradictory with delta. + + (arg_vars, theta_ev_vars, strict_arg_tys, ex_tvs2) <- mkOneConFull arg_tys con + + -- 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]. + ex_ev_vars <- equateTyVars ex_tvs1 ex_tvs2 + + let new_ty_cs = listToBag theta_ev_vars `unionBags` listToBag ex_ev_vars + let new_tm_cs = unitBag (TmVarCon x alt arg_vars) + + -- Now check satifiability + mb_delta <- pmIsSatisfiable delta new_tm_cs new_ty_cs strict_arg_tys + tracePm "refineToAltCon" (vcat [ ppr x + , ppr new_tm_cs + , ppr new_ty_cs + , ppr strict_arg_tys + , ppr delta + , ppr mb_delta ]) + case mb_delta of + Nothing -> pure Nothing + Just delta' -> pure (Just (markRefined delta' x, arg_vars)) + +-- | This is the only place that actualy increments 'vi_n_refines'. +markRefined :: Delta -> Id -> Delta +markRefined delta@MkDelta{ delta_tm_cs = ts@(TS env) } x + = delta{ delta_tm_cs = TS env' } + where + vi = lookupVarInfo ts x + env' = setEntrySDIE env x vi{ vi_n_refines = vi_n_refines vi + 1 } + +{- +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! +-} + +-------------------------------------- +-- * Term oracle unification procedure + +-- | Try to unify two 'Id's and record the gained knowledge in 'Delta'. +-- +-- Returns @Nothing@ when there's a contradiction. Returns @Just delta@ +-- when the constraint was compatible with prior facts, in which case @delta@ +-- has integrated the knowledge from the equality constraint. +-- +-- See Note [TmState invariants]. +addVarVarCt :: Delta -> (Id, Id) -> MaybeT DsM Delta +addVarVarCt delta@MkDelta{ delta_tm_cs = TS env } (x, y) + -- It's important that we never @equate@ two variables of the same equivalence + -- class, otherwise we might get cyclic substitutions. + -- Cf. 'extendSubstAndSolve' and + -- @testsuite/tests/pmcheck/should_compile/CyclicSubst.hs@. + | sameRepresentative env x y = pure delta + | otherwise = equate delta x y + +-- | @equate ts@(TS env) x y@ merges the equivalence classes of @x@ and @y@ by +-- adding an indirection to the environment. +-- Makes sure that the positive and negative facts of @x@ and @y@ are +-- compatible. +-- Preconditions: @not (sameRepresentative env x y)@ +-- +-- See Note [TmState invariants]. +equate :: Delta -> Id -> Id -> MaybeT DsM Delta +equate delta@MkDelta{ delta_tm_cs = TS env } x y + = ASSERT( not (sameRepresentative env x y) ) + case (lookupSDIE env x, lookupSDIE env y) of + (Nothing, _) -> pure (delta{ delta_tm_cs = TS (setIndirectSDIE env x y) }) + (_, Nothing) -> pure (delta{ delta_tm_cs = TS (setIndirectSDIE env y x) }) + -- Merge the info we have for x into the info for y + (Just vi_x, Just vi_y) -> do + -- This assert will probably trigger at some point... + -- We should decide how to break the tie + MASSERT2( vi_ty vi_x `eqType` vi_ty vi_y, text "Not same type" ) + -- First assume that x and y are in the same equivalence class + let env_ind = setIndirectSDIE env x y + -- Then sum up the refinement counters + let vi_y' = vi_y{ vi_n_refines = vi_n_refines vi_x + vi_n_refines vi_y } + let env_refs = setEntrySDIE env_ind y vi_y' + let delta_refs = delta{ delta_tm_cs = TS env_refs } + -- and then gradually merge every positive fact we have on x into y + let add_fact delta (cl, args) = addVarConCt delta y cl args + delta_pos <- foldlM add_fact delta_refs (vi_pos vi_x) + -- Do the same for negative info + let add_refut delta nalt = MaybeT (addRefutableAltCon delta y nalt) + delta_neg <- foldlM add_refut delta_pos (vi_neg vi_x) + -- vi_cache will be updated in addRefutableAltCon, so we are good to + -- go! + pure delta_neg + +-- | @addVarConCt x alt args ts@ extends the substitution with a mapping @x: -> +-- PmExprCon alt args@ if compatible with refutable shapes of @x@ and its +-- solution, reject (@Nothing@) otherwise. +-- +-- See Note [TmState invariants]. +addVarConCt :: Delta -> Id -> PmAltCon -> [Id] -> MaybeT DsM Delta +addVarConCt delta@MkDelta{ delta_tm_cs = TS env } x alt args = do + VI ty pos neg cache n <- lift (initLookupVarInfo delta x) + -- First try to refute with a negative fact + guard (all ((/= Equal) . eqPmAltCon alt) neg) + -- Then see if any of the other solutions (remember: each of them is an + -- additional refinement of the possible values x could take) indicate a + -- contradiction + guard (all ((/= Disjoint) . eqPmAltCon alt . fst) pos) + -- Now we should be good! Add (alt, args) as a possible solution, or refine an + -- existing one + case find ((== Equal) . eqPmAltCon alt . fst) pos of + Just (_, other_args) -> do + foldlM addVarVarCt delta (zip args other_args) + Nothing -> do + -- Filter out redundant negative facts (those that compare Just False to + -- the new solution) + let neg' = filter ((== PossiblyOverlap) . eqPmAltCon alt) neg + let pos' = (alt,args):pos + pure delta{ delta_tm_cs = TS (setEntrySDIE env x (VI ty pos' neg' cache n))} + +---------------------------------------- +-- * Enumerating inhabitation candidates + +-- | Information about a conlike that is relevant to coverage checking. +-- It is called an \"inhabitation candidate\" since it is a value which may +-- possibly inhabit some type, but only if its term constraints ('ic_tm_cs') +-- and type constraints ('ic_ty_cs') are permitting, and if all of its strict +-- argument types ('ic_strict_arg_tys') are inhabitable. +-- See @Note [Strict argument type constraints]@. +data InhabitationCandidate = + InhabitationCandidate + { ic_tm_cs :: Bag TmCt + , ic_ty_cs :: Bag EvVar + , ic_strict_arg_tys :: [Type] + } + +instance Outputable InhabitationCandidate where + ppr (InhabitationCandidate tm_cs ty_cs strict_arg_tys) = + text "InhabitationCandidate" <+> + vcat [ text "ic_tm_cs =" <+> ppr tm_cs + , text "ic_ty_cs =" <+> ppr ty_cs + , text "ic_strict_arg_tys =" <+> ppr strict_arg_tys ] + +mkInhabitationCandidate :: Id -> DataCon -> DsM InhabitationCandidate +-- Precondition: idType x is a TyConApp, so that tyConAppArgs in here is safe. +mkInhabitationCandidate x dc = do + let cl = RealDataCon dc + let tc_args = tyConAppArgs (idType x) + (arg_vars, ev_vars, strict_arg_tys, _ex_tyvars) <- mkOneConFull tc_args cl + pure InhabitationCandidate + { ic_tm_cs = unitBag (TmVarCon x (PmAltConLike cl) arg_vars) + , ic_ty_cs = listToBag ev_vars + , ic_strict_arg_tys = strict_arg_tys + } + +-- | Generate all 'InhabitationCandidate's for a given type. The result is +-- either @'Left' ty@, if the type cannot be reduced to a closed algebraic type +-- (or if it's one trivially inhabited, like 'Int'), or @'Right' candidates@, +-- if it can. In this case, the candidates are the signature of the tycon, each +-- one accompanied by the term- and type- constraints it gives rise to. +-- See also Note [Checking EmptyCase Expressions] +inhabitationCandidates :: Delta -> Type + -> DsM (Either Type (TyCon, Id, [InhabitationCandidate])) +inhabitationCandidates MkDelta{ delta_ty_cs = ty_cs } ty = do + pmTopNormaliseType ty_cs ty >>= \case + NoChange _ -> alts_to_check ty ty [] + NormalisedByConstraints ty' -> alts_to_check ty' ty' [] + HadRedexes src_ty dcs core_ty -> alts_to_check src_ty core_ty dcs + where + -- All these types are trivially inhabited + trivially_inhabited = [ charTyCon, doubleTyCon, floatTyCon + , intTyCon, wordTyCon, word8TyCon ] + + build_newtype :: (Type, DataCon) -> Id -> DsM (Id, TmCt) + build_newtype (ty, dc) x = do + -- ty is the type of @dc x@. It's a @dataConTyCon dc@ application. + y <- mkPmId ty + pure (y, TmVarCon y (PmAltConLike (RealDataCon dc)) [x]) + + build_newtypes :: Id -> [(Type, DataCon)] -> DsM (Id, [TmCt]) + build_newtypes x = foldrM (\dc (x, cts) -> go dc x cts) (x, []) + where + go dc x cts = second (:cts) <$> build_newtype dc x + + -- Inhabitation candidates, using the result of pmTopNormaliseType + alts_to_check :: Type -> Type -> [(Type, DataCon)] + -> DsM (Either Type (TyCon, Id, [InhabitationCandidate])) + alts_to_check src_ty core_ty dcs = case splitTyConApp_maybe core_ty of + Just (tc, _) + | tc `elem` trivially_inhabited + -> case dcs of + [] -> return (Left src_ty) + (_:_) -> do inner <- mkPmId core_ty + (outer, new_tm_cts) <- build_newtypes inner dcs + return $ Right (tc, outer, [InhabitationCandidate + { ic_tm_cs = listToBag new_tm_cts + , 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. + -> do + inner <- mkPmId core_ty -- it would be wrong to unify inner + alts <- mapM (mkInhabitationCandidate inner) (tyConDataCons tc) + (outer, new_tm_cts) <- build_newtypes inner dcs + let wrap_dcs alt = alt{ ic_tm_cs = listToBag new_tm_cts `unionBags` ic_tm_cs alt} + return $ Right (tc, outer, map wrap_dcs alts) + -- For other types conservatively assume that they are inhabited. + _other -> return (Left src_ty) + +inhabitants :: Delta -> Type -> DsM (Either Type (Id, [Delta])) +inhabitants delta ty = inhabitationCandidates delta ty >>= \case + Left ty' -> pure (Left ty') + Right (_, va, candidates) -> do + deltas <- flip mapMaybeM candidates $ + \InhabitationCandidate{ ic_tm_cs = tm_cs + , ic_ty_cs = ty_cs + , ic_strict_arg_tys = strict_arg_tys } -> do + pmIsSatisfiable delta tm_cs ty_cs strict_arg_tys + pure (Right (va, deltas)) + +---------------------------- +-- * Detecting vacuous types + +{- Note [Checking EmptyCase Expressions] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Empty case expressions are strict on the scrutinee. That is, `case x of {}` +will force argument `x`. Hence, `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 (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 Check.checkEmptyCase' performs the check: + - If core_ty is not an algebraic type, then we cannot check for + inhabitation, so we emit (_ :: src_ty) as missing, conservatively assuming + that the type is inhabited. + - If core_ty is an algebraic type, then we unfold the scrutinee to all + possible constructor patterns, using inhabitationCandidates, and then + check each one for constraint satisfiability, same as we do for normal + pattern match checking. +-} + +-- | A 'SatisfiabilityCheck' based on "NonVoid ty" constraints, e.g. Will +-- check if the @strict_arg_tys@ are actually all inhabited. +-- Returns the old 'Delta' if all the types are non-void according to 'Delta'. +tysAreNonVoid :: RecTcChecker -> [Type] -> SatisfiabilityCheck +tysAreNonVoid rec_env strict_arg_tys = SC $ \delta -> do + all_non_void <- checkAllNonVoid rec_env delta strict_arg_tys + -- Check if each strict argument type is inhabitable + pure $ if all_non_void + then Just delta + else Nothing + +-- | Implements two performance optimizations, as described in +-- @Note [Strict argument type constraints]@. +checkAllNonVoid :: RecTcChecker -> Delta -> [Type] -> DsM Bool +checkAllNonVoid rec_ts amb_cs strict_arg_tys = do + let definitely_inhabited = definitelyInhabitedType (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 [Strict argument type constraints]@. +nonVoid + :: RecTcChecker -- ^ The per-'TyCon' recursion depth limit. + -> Delta -- ^ The ambient term/type constraints (known to be + -- satisfiable). + -> Type -- ^ The strict argument type. + -> DsM Bool -- ^ 'True' if the strict argument type might be inhabited by + -- a terminating value (i.e., an 'InhabitationCandidate'). + -- 'False' if it is definitely uninhabitable by anything + -- (except bottom). +nonVoid rec_ts amb_cs strict_arg_ty = do + mb_cands <- inhabitationCandidates amb_cs strict_arg_ty + case mb_cands of + Right (tc, _, cands) + | Just rec_ts' <- checkRecTc rec_ts tc + -> anyM (cand_is_inhabitable rec_ts' amb_cs) cands + -- A strict argument type is inhabitable by a terminating value if + -- at least one InhabitationCandidate is inhabitable. + _ -> pure True + -- Either the type is trivially inhabited or we have exceeded the + -- recursion depth for some TyCon (so bail out and conservatively + -- claim the type is inhabited). + where + -- Checks if an InhabitationCandidate for a strict argument type: + -- + -- (1) Has satisfiable term and type constraints. + -- (2) Has 'nonVoid' strict argument types (we bail out of this + -- check if recursion is detected). + -- + -- See Note [Strict argument type constraints] + cand_is_inhabitable :: RecTcChecker -> Delta + -> InhabitationCandidate -> DsM Bool + cand_is_inhabitable rec_ts amb_cs + (InhabitationCandidate{ ic_tm_cs = new_tm_cs + , ic_ty_cs = new_ty_cs + , ic_strict_arg_tys = new_strict_arg_tys }) = + fmap isJust $ runSatisfiabilityCheck amb_cs $ mconcat + [ tyIsSatisfiable False new_ty_cs + , tmIsSatisfiable new_tm_cs + , tysAreNonVoid rec_ts new_strict_arg_tys + ] + +-- | @'definitelyInhabitedType' ty@ returns 'True' if @ty@ has at least one +-- constructor @C@ such that: +-- +-- 1. @C@ has no equality constraints. +-- 2. @C@ has no strict argument types. +-- +-- See the @Note [Strict argument type constraints]@. +definitelyInhabitedType :: Bag EvVar -> Type -> DsM Bool +definitelyInhabitedType ty_cs ty = do + res <- pmTopNormaliseType ty_cs ty + pure $ case res of + HadRedexes _ cons _ -> any meets_criteria cons + _ -> False + where + meets_criteria :: (Type, DataCon) -> Bool + meets_criteria (_, con) = + null (dataConEqSpec con) && -- (1) + null (dataConImplBangs con) -- (2) + +{- Note [Strict argument type constraints] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In the ConVar case of clause processing, each conlike K traditionally +generates two different forms of constraints: + +* A term constraint (e.g., x ~ K y1 ... yn) +* Type constraints from the conlike's context (e.g., if K has type + forall bs. Q => s1 .. sn -> T tys, then Q would be its type constraints) + +As it turns out, these alone are not enough to detect a certain class of +unreachable code. Consider the following example (adapted from #15305): + + data K = K1 | K2 !Void + + f :: K -> () + f K1 = () + +Even though `f` doesn't match on `K2`, `f` is exhaustive in its patterns. Why? +Because it's impossible to construct a terminating value of type `K` using the +`K2` constructor, and thus it's impossible for `f` to ever successfully match +on `K2`. + +The reason is because `K2`'s field of type `Void` is //strict//. Because there +are no terminating values of type `Void`, any attempt to construct something +using `K2` will immediately loop infinitely or throw an exception due to the +strictness annotation. (If the field were not strict, then `f` could match on, +say, `K2 undefined` or `K2 (let x = x in x)`.) + +Since neither the term nor type constraints mentioned above take strict +argument types into account, we make use of the `nonVoid` function to +determine whether a strict type is inhabitable by a terminating value or not. + +`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 check if a type is DI, so before we call `nonVoid` +on a list of strict argument types, we filter out all of the DI ones. +-} + +-------------------------------------------- +-- * Providing positive evidence for a Delta + +-- | @provideEvidenceForEquation vs n delta@ returns a list of +-- at most @n@ (but perhaps empty) refinements of @delta@ that instantiate +-- @vs@ to compatible constructor applications or wildcards. +-- Negative information is only retained if literals are involved or when +-- for recursive GADTs. +provideEvidenceForEquation :: [Id] -> Int -> Delta -> DsM [Delta] +provideEvidenceForEquation = go init_ts + where + -- Choosing 1 here will not be enough for RedBlack, but any other bound + -- might potentially lead to combinatorial explosion, so we are extremely + -- cautious and pick 2 here. + init_ts = setRecTcMaxBound 2 initRecTc + go _ _ 0 _ = pure [] + go _ [] _ delta = pure [delta] + go rec_ts (x:xs) n delta = do + VI ty pos neg pm _ <- initLookupVarInfo delta x + case pos of + _:_ -> do + -- All solutions must be valid at once. Try to find candidates for their + -- fields. Example: + -- f x@(Just _) True = case x of SomePatSyn _ -> () + -- after this clause, we want to report that + -- * @f Nothing _@ is uncovered + -- * @f x False@ is uncovered + -- where @x@ will have two possibly compatible solutions, @Just y@ for + -- some @y@ and @SomePatSyn z@ for some @z@. We must find evidence for @y@ + -- and @z@ that is valid at the same time. These constitute arg_vas below. + let arg_vas = concatMap (\(_cl, args) -> args) pos + go rec_ts (arg_vas ++ xs) n delta + [] + -- First the simple case where we don't need to query the oracles + | isVanillaDataType ty + -- So no type info unleashed in pattern match + , null neg + -- No term-level info either + || notNull [ l | PmAltLit l <- neg ] + -- ... or there are literals involved, in which case we don't want + -- to split on possible constructors + -> go rec_ts xs n delta + [] -> do + -- We have to pick one of the available constructors and try it + -- It's important that each of the ConLikeSets in pm is still inhabited, + -- so that it doesn't matter from which we pick. + -- I think we implicitly uphold that invariant, but not too sure + case getUnmatchedConstructor pm of + Unsatisfiable -> pure [] + -- No COMPLETE sets available, so we can assume it's inhabited + PossiblySatisfiable -> go rec_ts xs n delta + Satisfiable cl + | Just rec_ts' <- checkRecTc rec_ts (fst (splitTyConApp ty)) + -> split_at_con rec_ts' delta n x xs cl + | otherwise + -- We ran out of fuel; just conservatively assume that this is + -- inhabited. + -> go rec_ts xs n delta + + -- | @split_at_con _ delta _ x _ con@ splits the given delta into two + -- models: One where we assume x is con and one where we assume it can't be + -- con. Really similar to the ConVar case in Check, only that we don't + -- really have a pattern driving the split. + split_at_con + :: RecTcChecker -- ^ Detects when we split the same TyCon too often + -> Delta -- ^ The model we like to refine to the split + -> Int -- ^ The number of equations still to produce + -> Id -> [Id] -- ^ Head and tail of the value abstractions + -> ConLike -- ^ The ConLike over which to split + -> DsM [Delta] + split_at_con rec_ts delta n x xs cl = do + -- This will be really similar to the ConVar case + let (_,ex_tvs,_,_,_,_,_) = conLikeFullSig cl + -- we might need to freshen ex_tvs. Not sure + -- We may need to reduce type famlies/synonyms in x's type first + res <- pmTopNormaliseType (delta_ty_cs delta) (idType x) + let res_ty = normalisedSourceType res + env <- dsGetFamInstEnvs + case guessConLikeUnivTyArgsFromResTy env res_ty cl of + Nothing -> pure [delta] -- We can't split this one, so assume it's inhabited + Just arg_tys -> do + ev_pos <- refineToAltCon delta x (PmAltConLike cl) arg_tys ex_tvs >>= \case + Nothing -> pure [] + Just (delta', arg_vas) -> + go rec_ts (arg_vas ++ xs) n delta' + + -- Only n' more equations to go... + let n' = n - length ev_pos + ev_neg <- addRefutableAltCon delta x (PmAltConLike cl) >>= \case + Nothing -> pure [] + Just delta' -> go rec_ts (x:xs) n' delta' + + -- Actually there was no need to split if we see that both branches + -- were inhabited and we had no negative information on the variable! + -- So only refine delta if we find that one branch was indeed + -- uninhabited. + let neg = lookupRefuts delta x + case (ev_pos, ev_neg) of + ([], _) -> pure ev_neg + (_, []) -> pure ev_pos + _ | null neg -> pure [delta] + | otherwise -> pure (ev_pos ++ ev_neg) + +-- | Checks if every data con of the type 'isVanillaDataCon'. +isVanillaDataType :: Type -> Bool +isVanillaDataType ty = fromMaybe False $ do + (tc, _) <- splitTyConApp_maybe ty + dcs <- tyConDataCons_maybe tc + pure (all isVanillaDataCon dcs) + +-- Most of our actions thread around a delta from one computation to the next, +-- thereby potentially failing. This is expressed in the following Monad: +-- type PmM a = StateT Delta (MaybeT DsM) a + +-- | Records that a variable @x@ is equal to a 'CoreExpr' @e@. +addVarCoreCt :: Delta -> Id -> CoreExpr -> DsM (Maybe Delta) +addVarCoreCt delta x e = runMaybeT (execStateT (core_expr x e) delta) + where + -- | Takes apart a 'CoreExpr' and tries to extract as much information about + -- literals and constructor applications as possible. + core_expr :: Id -> CoreExpr -> StateT Delta (MaybeT DsM) () + -- TODO: Handle newtypes properly, by wrapping the expression in a DataCon + core_expr x (Cast e _co) = core_expr x e + core_expr x (Tick _t e) = core_expr x e + core_expr x e + | Just (pmLitAsStringLit -> Just s) <- coreExprAsPmLit e + , expr_ty `eqType` stringTy + -- See Note [Representation of Strings in TmState] + = case unpackFS s of + -- We need this special case to break a loop with coreExprAsPmLit + -- Otherwise we alternate endlessly between [] and "" + [] -> data_con_app x nilDataCon [] + s' -> core_expr x (mkListExpr charTy (map mkCharExpr s')) + | Just lit <- coreExprAsPmLit e + = pm_lit x lit + | Just (_in_scope, _empty_floats@[], dc, _arg_tys, args) + <- exprIsConApp_maybe empty_in_scope e + = do { arg_ids <- traverse bind_expr args + ; data_con_app x dc arg_ids } + -- TODO: Think about how to recognize PatSyns + | Var y <- e, not (isDataConWorkId x) + = modifyT (\delta -> addVarVarCt delta (x, y)) + | otherwise + -- TODO: Use a CoreMap to identify the CoreExpr with a unique representant + = pure () + where + empty_in_scope = (emptyInScopeSet, const NoUnfolding) + expr_ty = exprType e + + bind_expr :: CoreExpr -> StateT Delta (MaybeT DsM) Id + bind_expr e = do + x <- lift (lift (mkPmId (exprType e))) + core_expr x e + pure x + + data_con_app :: Id -> DataCon -> [Id] -> StateT Delta (MaybeT DsM) () + data_con_app x dc args = pm_alt_con_app x (PmAltConLike (RealDataCon dc)) args + + pm_lit :: Id -> PmLit -> StateT Delta (MaybeT DsM) () + pm_lit x lit = pm_alt_con_app x (PmAltLit lit) [] + + -- | Adds the given constructor application as a solution for @x@. + pm_alt_con_app :: Id -> PmAltCon -> [Id] -> StateT Delta (MaybeT DsM) () + pm_alt_con_app x con args = modifyT $ \delta -> addVarConCt delta x con args + +-- | Like 'modify', but with an effectful modifier action +modifyT :: Monad m => (s -> m s) -> StateT s m () +modifyT f = StateT $ fmap ((,) ()) . f diff --git a/compiler/deSugar/PmOracle.hs-boot b/compiler/deSugar/PmOracle.hs-boot new file mode 100644 index 0000000000..e099fe78a6 --- /dev/null +++ b/compiler/deSugar/PmOracle.hs-boot @@ -0,0 +1,7 @@ +module PmOracle where + +import GhcPrelude () + +data Delta + +initDelta :: Delta diff --git a/compiler/deSugar/PmPpr.hs b/compiler/deSugar/PmPpr.hs index 06b60a6806..bee38ed46b 100644 --- a/compiler/deSugar/PmPpr.hs +++ b/compiler/deSugar/PmPpr.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP, ViewPatterns #-} -- | Provides factilities for pretty-printing 'PmExpr's in a way approriate for -- user facing pattern match warnings. @@ -10,20 +10,20 @@ module PmPpr ( import GhcPrelude -import Name -import NameEnv -import NameSet +import Id +import VarEnv import UniqDFM -import UniqSet import ConLike import DataCon import TysWiredIn import Outputable -import Control.Monad.Trans.State.Strict -import Maybes +import Control.Monad.Trans.RWS.CPS import Util +import Maybes +import Data.List.NonEmpty (NonEmpty, nonEmpty, toList) -import TmOracle +import PmExpr +import PmOracle -- | Pretty-print the guts of an uncovered value vector abstraction, i.e., its -- components and refutable shapes associated to any mentioned variables. @@ -35,22 +35,31 @@ import TmOracle -- where p is not one of {3, 4} -- q is not one of {0, 5} -- @ -pprUncovered :: ([PmExpr], PmRefutEnv) -> SDoc -pprUncovered (expr_vec, refuts) - | null cs = fsep vec -- there are no literal constraints - | otherwise = hang (fsep vec) 4 $ - text "where" <+> vcat (map pprRefutableShapes cs) +-- +-- When the set of refutable shapes contains more than 3 elements, the +-- additional elements are indicated by "...". +pprUncovered :: Delta -> [Id] -> SDoc +pprUncovered delta vas + | isNullUDFM refuts = fsep vec -- there are no refutations + | otherwise = hang (fsep vec) 4 $ + text "where" <+> vcat (map (pprRefutableShapes . snd) (udfmToList refuts)) where - sdoc_vec = mapM pprPmExprWithParens expr_vec - (vec,cs) = runPmPpr sdoc_vec (prettifyRefuts refuts) + ppr_action = mapM (pprPmExprVar 2) vas + (vec, renamings) = runPmPpr delta ppr_action + refuts = prettifyRefuts delta renamings -- | Output refutable shapes of a variable in the form of @var is not one of {2, --- Nothing, 3}@. +-- Nothing, 3}@. Will never print more than 3 refutable shapes, the tail is +-- indicated by an ellipsis. pprRefutableShapes :: (SDoc,[PmAltCon]) -> SDoc pprRefutableShapes (var, alts) - = var <+> text "is not one of" <+> braces (pprWithCommas ppr_alt alts) + = var <+> text "is not one of" <+> format_alts alts where - ppr_alt (PmAltLit lit) = ppr lit + format_alts = braces . fsep . punctuate comma . shorten . map ppr_alt + shorten (a:b:c:_:_) = a:b:c:[text "..."] + shorten xs = xs + ppr_alt (PmAltConLike cl) = ppr cl + ppr_alt (PmAltLit lit) = ppr lit {- 1. Literals ~~~~~~~~~~~~~~ @@ -78,114 +87,115 @@ substitution to the vectors before printing them out (see function `pprOne' in Check.hs) to be more precise. -} --- | A 'PmRefutEnv' with pretty names for the occuring variables. -type PrettyPmRefutEnv = DNameEnv (SDoc, [PmAltCon]) - --- | Assigns pretty names to constraint variables in the domain of the given --- 'PmRefutEnv'. -prettifyRefuts :: PmRefutEnv -> PrettyPmRefutEnv -prettifyRefuts = listToUDFM . zipWith rename nameList . udfmToList +-- | Extract and assigns pretty names to constraint variables with refutable +-- shapes. +prettifyRefuts :: Delta -> DIdEnv SDoc -> DIdEnv (SDoc, [PmAltCon]) +prettifyRefuts delta = listToUDFM . map attach_refuts . udfmToList where - rename new (old, lits) = (old, (new, lits)) - -- Try nice names p,q,r,s,t before using the (ugly) t_i - nameList :: [SDoc] - nameList = map text ["p","q","r","s","t"] ++ - [ text ('t':show u) | u <- [(0 :: Int)..] ] - -type PmPprM a = State (PrettyPmRefutEnv, NameSet) a --- (the first part of the state is read only. make it a reader?) - -runPmPpr :: PmPprM a -> PrettyPmRefutEnv -> (a, [(SDoc,[PmAltCon])]) -runPmPpr m lit_env = (result, mapMaybe is_used (udfmToList lit_env)) - where - (result, (_lit_env, used)) = runState m (lit_env, emptyNameSet) - - is_used (k,v) - | elemUniqSet_Directly k used = Just v - | otherwise = Nothing - -addUsed :: Name -> PmPprM () -addUsed x = modify (\(negated, used) -> (negated, extendNameSet used x)) - -checkNegation :: Name -> PmPprM (Maybe SDoc) -- the clean name if it is negated -checkNegation x = do - negated <- gets fst - return $ case lookupDNameEnv negated x of - Just (new, _) -> Just new - Nothing -> Nothing - --- | Pretty print a pmexpr, but remember to prettify the names of the variables + attach_refuts (u, sdoc) = (u, (sdoc, lookupRefuts delta u)) + + +type PmPprM a = RWS Delta () (DIdEnv SDoc, [SDoc]) a + +-- Try nice names p,q,r,s,t before using the (ugly) t_i +nameList :: [SDoc] +nameList = map text ["p","q","r","s","t"] ++ + [ text ('t':show u) | u <- [(0 :: Int)..] ] + +runPmPpr :: Delta -> PmPprM a -> (a, DIdEnv SDoc) +runPmPpr delta m = case runRWS m delta (emptyDVarEnv, nameList) of + (a, (renamings, _), _) -> (a, renamings) + +-- | Allocates a new, clean name for the given 'Id' if it doesn't already have +-- one. +getCleanName :: Id -> PmPprM SDoc +getCleanName x = do + (renamings, name_supply) <- get + let (clean_name:name_supply') = name_supply + case lookupDVarEnv renamings x of + Just nm -> pure nm + Nothing -> do + put (extendDVarEnv renamings x clean_name, name_supply') + pure clean_name + +checkRefuts :: Id -> PmPprM (Maybe SDoc) -- the clean name if it has negative info attached +checkRefuts x = do + delta <- ask + case lookupRefuts delta x of + [] -> pure Nothing -- Will just be a wildcard later on + _ -> Just <$> getCleanName x + +-- | Pretty print a variable, but remember to prettify the names of the variables -- that refer to neg-literals. The ones that cannot be shown are printed as -- underscores. -pprPmExpr :: PmExpr -> PmPprM SDoc -pprPmExpr (PmExprVar x) = do - mb_name <- checkNegation x - case mb_name of - Just name -> addUsed x >> return name - Nothing -> return underscore -pprPmExpr (PmExprCon con args) = pprPmExprCon con args -pprPmExpr (PmExprLit l) = return (ppr l) -pprPmExpr (PmExprOther _) = return underscore -- don't show - -needsParens :: PmExpr -> Bool -needsParens (PmExprVar {}) = False -needsParens (PmExprLit l) = isNegatedPmLit l -needsParens (PmExprOther {}) = False -- will become a wildcard -needsParens (PmExprCon (RealDataCon c) es) - | isTupleDataCon c - || isConsDataCon c || null es = False - | otherwise = True -needsParens (PmExprCon (PatSynCon _) es) = not (null es) - -pprPmExprWithParens :: PmExpr -> PmPprM SDoc -pprPmExprWithParens expr - | needsParens expr = parens <$> pprPmExpr expr - | otherwise = pprPmExpr expr - -pprPmExprCon :: ConLike -> [PmExpr] -> PmPprM SDoc -pprPmExprCon (RealDataCon con) args - | isTupleDataCon con = mkTuple <$> mapM pprPmExpr args - | isConsDataCon con = pretty_list - where - mkTuple :: [SDoc] -> SDoc - mkTuple = parens . fsep . punctuate comma - - -- lazily, to be used in the list case only - pretty_list :: PmPprM SDoc - pretty_list = case isNilPmExpr (last list) of - True -> brackets . fsep . punctuate comma <$> mapM pprPmExpr (init list) - False -> parens . hcat . punctuate colon <$> mapM pprPmExpr list - - list = list_elements args - - list_elements [x,y] - | PmExprCon c es <- y, RealDataCon nilDataCon == c - = ASSERT(null es) [x,y] - | PmExprCon c es <- y, RealDataCon consDataCon == c - = x : list_elements es - | otherwise = [x,y] - list_elements list = pprPanic "list_elements:" (ppr list) -pprPmExprCon cl args +pprPmExprVar :: Int -> Id -> PmPprM SDoc +pprPmExprVar prec x = do + delta <- ask + case lookupSolution delta x of + Just (alt, args) -> pprPmExprCon prec alt args + Nothing -> fromMaybe underscore <$> checkRefuts x + +pprPmExprCon :: Int -> PmAltCon -> [Id] -> PmPprM SDoc +pprPmExprCon _prec (PmAltLit l) _ = pure (ppr l) +pprPmExprCon prec (PmAltConLike cl) args = do + delta <- ask + pprConLike delta prec cl args + +pprConLike :: Delta -> Int -> ConLike -> [Id] -> PmPprM SDoc +pprConLike delta _prec cl args + | Just pm_expr_list <- pmExprAsList delta (PmAltConLike cl) args + = case pm_expr_list of + NilTerminated list -> + brackets . fsep . punctuate comma <$> mapM (pprPmExprVar 0) list + WcVarTerminated pref x -> + parens . fcat . punctuate colon <$> mapM (pprPmExprVar 0) (toList pref ++ [x]) +pprConLike _delta _prec (RealDataCon con) args + | isUnboxedTupleCon con + , let hash_parens doc = text "(#" <+> doc <+> text "#)" + = hash_parens . fsep . punctuate comma <$> mapM (pprPmExprVar 0) args + | isTupleDataCon con + = parens . fsep . punctuate comma <$> mapM (pprPmExprVar 0) args +pprConLike _delta prec cl args | conLikeIsInfix cl = case args of - [x, y] -> do x' <- pprPmExprWithParens x - y' <- pprPmExprWithParens y - return (x' <+> ppr cl <+> y') + [x, y] -> do x' <- pprPmExprVar 1 x + y' <- pprPmExprVar 1 y + return (cparen (prec > 0) (x' <+> ppr cl <+> y')) -- can it be infix but have more than two arguments? list -> pprPanic "pprPmExprCon:" (ppr list) | null args = return (ppr cl) - | otherwise = do args' <- mapM pprPmExprWithParens args - return (fsep (ppr cl : args')) - --- | Check whether a literal is negated -isNegatedPmLit :: PmLit -> Bool -isNegatedPmLit (PmOLit b _) = b -isNegatedPmLit _other_lit = False - --- | Check whether a PmExpr is syntactically e -isNilPmExpr :: PmExpr -> Bool -isNilPmExpr (PmExprCon c _) = c == RealDataCon nilDataCon -isNilPmExpr _other_expr = False - --- | Check if a DataCon is (:). -isConsDataCon :: DataCon -> Bool -isConsDataCon con = consDataCon == con + | otherwise = do args' <- mapM (pprPmExprVar 2) args + return (cparen (prec > 1) (fsep (ppr cl : args'))) + +-- | The result of 'pmExprAsList'. +data PmExprList + = NilTerminated [Id] + | WcVarTerminated (NonEmpty Id) Id + +-- | Extract a list of 'PmExpr's out of a sequence of cons cells, optionally +-- terminated by a wildcard variable instead of @[]@. Some examples: +-- +-- * @pmExprAsList (1:2:[]) == Just ('NilTerminated' [1,2])@, a regular, +-- @[]@-terminated list. Should be pretty-printed as @[1,2]@. +-- * @pmExprAsList (1:2:x) == Just ('WcVarTerminated' [1,2] x)@, a list prefix +-- ending in a wildcard variable x (of list type). Should be pretty-printed as +-- (1:2:_). +-- * @pmExprAsList [] == Just ('NilTerminated' [])@ +pmExprAsList :: Delta -> PmAltCon -> [Id] -> Maybe PmExprList +pmExprAsList delta = go_con [] + where + go_var rev_pref x + | Just (alt, args) <- lookupSolution delta x + = go_con rev_pref alt args + go_var rev_pref x + | Just pref <- nonEmpty (reverse rev_pref) + = Just (WcVarTerminated pref x) + go_var _ _ + = Nothing + + go_con rev_pref (PmAltConLike (RealDataCon c)) es + | c == nilDataCon + = ASSERT( null es ) Just (NilTerminated (reverse rev_pref)) + | c == consDataCon + = ASSERT( length es == 2 ) go_var (es !! 0 : rev_pref) (es !! 1) + go_con _ _ _ + = Nothing diff --git a/compiler/deSugar/TmOracle.hs b/compiler/deSugar/TmOracle.hs deleted file mode 100644 index db3ce6d770..0000000000 --- a/compiler/deSugar/TmOracle.hs +++ /dev/null @@ -1,362 +0,0 @@ -{- -Author: George Karachalias <george.karachalias@cs.kuleuven.be> --} - -{-# LANGUAGE CPP, MultiWayIf #-} - --- | The term equality oracle. The main export of the module are the functions --- 'tmOracle', 'solveOneEq' and 'addSolveRefutableAltCon'. --- --- If you are looking for an oracle that can solve type-level constraints, look --- at 'TcSimplify.tcCheckSatisfiability'. -module TmOracle ( - - -- re-exported from PmExpr - PmExpr(..), PmLit(..), PmAltCon(..), TmVarCt(..), TmVarCtEnv, - PmRefutEnv, eqPmLit, isNotPmExprOther, lhsExprToPmExpr, hsExprToPmExpr, - - -- the term oracle - tmOracle, TmState, initialTmState, wrapUpTmState, solveOneEq, - extendSubst, canDiverge, isRigid, - addSolveRefutableAltCon, lookupRefutableAltCons, - - -- misc. - exprDeepLookup, pmLitType - ) where - -#include "HsVersions.h" - -import GhcPrelude - -import PmExpr - -import Util -import Id -import Name -import Type -import HsLit -import TcHsSyn -import MonadUtils -import ListSetOps (insertNoDup, unionLists) -import Maybes -import Outputable -import NameEnv -import UniqFM -import UniqDFM - -{- -%************************************************************************ -%* * - The term equality oracle -%* * -%************************************************************************ --} - --- | Pretty much a @['TmVarCt']@ association list where the domain is 'Name' --- instead of 'Id'. This is the type of 'tm_pos', where we store solutions for --- rigid pattern match variables. -type TmVarCtEnv = NameEnv PmExpr - --- | An environment assigning shapes to variables that immediately lead to a --- refutation. So, if this maps @x :-> [3]@, then trying to solve a 'TmVarCt' --- like @x ~ 3@ immediately leads to a contradiction. --- Determinism is important since we use this for warning messages in --- 'PmPpr.pprUncovered'. We don't do the same for 'TmVarCtEnv', so that is a plain --- 'NameEnv'. --- --- See also Note [Refutable shapes] in TmOracle. -type PmRefutEnv = DNameEnv [PmAltCon] - --- | The state of the term oracle. Tracks all term-level facts of the form "x is --- @True@" ('tm_pos') and "x is not @5@" ('tm_neg'). --- --- Subject to Note [The Pos/Neg invariant]. -data TmState = TmS - { tm_pos :: !TmVarCtEnv - -- ^ A substitution with solutions we extend with every step and return as a - -- result. The substitution is in /triangular form/: It might map @x@ to @y@ - -- where @y@ itself occurs in the domain of 'tm_pos', rendering lookup - -- non-idempotent. This means that 'varDeepLookup' potentially has to walk - -- along a chain of var-to-var mappings until we find the solution but has the - -- advantage that when we update the solution for @y@ above, we automatically - -- update the solution for @x@ in a union-find-like fashion. - -- Invariant: Only maps to other variables ('PmExprVar') or to WHNFs - -- ('PmExprLit', 'PmExprCon'). Ergo, never maps to a 'PmExprOther'. - , tm_neg :: !PmRefutEnv - -- ^ Maps each variable @x@ to a list of 'PmAltCon's that @x@ definitely - -- cannot match. Example, @x :-> [3, 4]@ means that @x@ cannot match a literal - -- 3 or 4. Should we later solve @x@ to a variable @y@ - -- ('extendSubstAndSolve'), we merge the refutable shapes of @x@ into those of - -- @y@. See also Note [The Pos/Neg invariant]. - } - -{- Note [The Pos/Neg invariant] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Invariant: In any 'TmState', The domains of 'tm_pos' and 'tm_neg' are disjoint. - -For example, it would make no sense to say both - tm_pos = [...x :-> 3 ...] - tm_neg = [...x :-> [4,42]... ] -The positive information is strictly more informative than the negative. - -Suppose we are adding the (positive) fact @x :-> e@ to 'tm_pos'. Then we must -delete any binding for @x@ from 'tm_neg', to uphold the invariant. - -But there is more! Suppose we are adding @x :-> y@ to 'tm_pos', and 'tm_neg' -contains @x :-> cs, y :-> ds@. Then we want to update 'tm_neg' to -@y :-> (cs ++ ds)@, to make use of the negative information we have about @x@. --} - -instance Outputable TmState where - ppr state = braces (fsep (punctuate comma (pos ++ neg))) - where - pos = map pos_eq (nonDetUFMToList (tm_pos state)) - neg = map neg_eq (udfmToList (tm_neg state)) - pos_eq (l, r) = ppr l <+> char '~' <+> ppr r - neg_eq (l, r) = ppr l <+> char '≁' <+> ppr r - --- | Initial state of the oracle. -initialTmState :: TmState -initialTmState = TmS emptyNameEnv emptyDNameEnv - --- | Wrap up the term oracle's state once solving is complete. Return the --- flattened 'tm_pos' and 'tm_neg'. -wrapUpTmState :: TmState -> (TmVarCtEnv, PmRefutEnv) -wrapUpTmState solver_state - = (flattenTmVarCtEnv (tm_pos solver_state), tm_neg solver_state) - --- | Flatten the triangular subsitution. -flattenTmVarCtEnv :: TmVarCtEnv -> TmVarCtEnv -flattenTmVarCtEnv env = mapNameEnv (exprDeepLookup env) env - --- | Check whether a constraint (x ~ BOT) can succeed, --- given the resulting state of the term oracle. -canDiverge :: Name -> TmState -> Bool -canDiverge x TmS{ tm_pos = pos, tm_neg = neg } - -- If the variable seems not evaluated, there is a possibility for - -- constraint x ~ BOT to be satisfiable. That's the case when we haven't found - -- a solution (i.e. some equivalent literal or constructor) for it yet. - | (_, PmExprVar y) <- varDeepLookup pos x -- seems not forced - -- Even if we don't have a solution yet, it might be involved in a negative - -- constraint, in which case we must already have evaluated it earlier. - , Nothing <- lookupDNameEnv neg y - = True - -- Variable x is already in WHNF or we know some refutable shape, so the - -- constraint is non-satisfiable - | otherwise = False - --- | Check whether the equality @x ~ e@ leads to a refutation. Make sure that --- @x@ and @e@ are completely substituted before! -isRefutable :: Name -> PmExpr -> PmRefutEnv -> Bool -isRefutable x e env - = fromMaybe False $ elem <$> exprToAlt e <*> lookupDNameEnv env x - --- | Solve an equality (top-level). -solveOneEq :: TmState -> TmVarCt -> Maybe TmState -solveOneEq solver_env (TVC x e) = unify solver_env (PmExprVar (idName x), e) - -exprToAlt :: PmExpr -> Maybe PmAltCon -exprToAlt (PmExprLit l) = Just (PmAltLit l) -exprToAlt _ = Nothing - --- | Record that a particular 'Id' can't take the shape of a 'PmAltCon' in the --- 'TmState' and return @Nothing@ if that leads to a contradiction. -addSolveRefutableAltCon :: TmState -> Id -> PmAltCon -> Maybe TmState -addSolveRefutableAltCon original@TmS{ tm_pos = pos, tm_neg = neg } x nalt - = case exprToAlt e of - -- We have to take care to preserve Note [The Pos/Neg invariant] - Nothing -> Just extended -- Not solved yet - Just alt -- We have a solution - | alt == nalt -> Nothing -- ... which is contradictory - | otherwise -> Just original -- ... which is compatible, rendering the - where -- refutation redundant - (y, e) = varDeepLookup pos (idName x) - extended = original { tm_neg = neg' } - neg' = alterDNameEnv (delNulls (insertNoDup nalt)) neg y - --- | When updating 'tm_neg', we want to delete any 'null' entries. This adapter --- intends to provide a suitable interface for 'alterDNameEnv'. -delNulls :: ([a] -> [a]) -> Maybe [a] -> Maybe [a] -delNulls f mb_entry - | ret@(_:_) <- f (fromMaybe [] mb_entry) = Just ret - | otherwise = Nothing - --- | Return all 'PmAltCon' shapes that are impossible for 'Id' to take, i.e. --- would immediately lead to a refutation by the term oracle. -lookupRefutableAltCons :: Id -> TmState -> [PmAltCon] -lookupRefutableAltCons x TmS { tm_neg = neg } - = fromMaybe [] (lookupDNameEnv neg (idName x)) - --- | Is the given variable /rigid/ (i.e., we have a solution for it) or --- /flexible/ (i.e., no solution)? Returns the solution if /rigid/. A --- semantically helpful alias for 'lookupNameEnv'. -isRigid :: TmState -> Name -> Maybe PmExpr -isRigid TmS{ tm_pos = pos } x = lookupNameEnv pos x - --- | @isFlexible tms = isNothing . 'isRigid' tms@ -isFlexible :: TmState -> Name -> Bool -isFlexible tms = isNothing . isRigid tms - --- | Try to unify two 'PmExpr's and record the gained knowledge in the --- 'TmState'. --- --- Returns @Nothing@ when there's a contradiction. Returns @Just tms@ --- when the constraint was compatible with prior facts, in which case @tms@ has --- integrated the knowledge from the equality constraint. -unify :: TmState -> (PmExpr, PmExpr) -> Maybe TmState -unify tms eq@(e1, e2) = case eq of - -- We cannot do a thing about these cases - (PmExprOther _,_) -> boring - (_,PmExprOther _) -> boring - - (PmExprLit l1, PmExprLit l2) -> case eqPmLit l1 l2 of - -- See Note [Undecidable Equality for Overloaded Literals] - True -> boring - False -> unsat - - (PmExprCon c1 ts1, PmExprCon c2 ts2) - | c1 == c2 -> foldlM unify tms (zip ts1 ts2) - | otherwise -> unsat - - (PmExprVar x, PmExprVar y) - | x == y -> boring - - -- It's important to handle both rigid cases first, otherwise we get cyclic - -- substitutions. Cf. 'extendSubstAndSolve' and - -- @testsuite/tests/pmcheck/should_compile/CyclicSubst.hs@. - (PmExprVar x, _) - | Just e1' <- isRigid tms x -> unify tms (e1', e2) - (_, PmExprVar y) - | Just e2' <- isRigid tms y -> unify tms (e1, e2') - (PmExprVar x, PmExprVar y) -> Just (equate x y tms) - (PmExprVar x, _) -> trySolve x e2 tms - (_, PmExprVar y) -> trySolve y e1 tms - - _ -> WARN( True, text "unify: Catch all" <+> ppr eq) - boring -- I HATE CATCH-ALLS - where - boring = Just tms - unsat = Nothing - --- | Merges the equivalence classes of @x@ and @y@ by extending the substitution --- with @x :-> y@. --- Preconditions: @x /= y@ and both @x@ and @y@ are flexible (cf. --- 'isFlexible'/'isRigid'). -equate :: Name -> Name -> TmState -> TmState -equate x y tms@TmS{ tm_pos = pos, tm_neg = neg } - = ASSERT( x /= y ) - ASSERT( isFlexible tms x ) - ASSERT( isFlexible tms y ) - tms' - where - pos' = extendNameEnv pos x (PmExprVar y) - -- Be careful to uphold Note [The Pos/Neg invariant] by merging the refuts - -- of x into those of y - nalts = fromMaybe [] (lookupDNameEnv neg x) - neg' = alterDNameEnv (delNulls (unionLists nalts)) neg y - `delFromDNameEnv` x - tms' = TmS { tm_pos = pos', tm_neg = neg' } - --- | Extend the substitution with a mapping @x: -> e@ if compatible with --- refutable shapes of @x@ and its solution, reject (@Nothing@) otherwise. --- --- Precondition: @x@ is flexible (cf. 'isFlexible'/'isRigid'). --- Precondition: @e@ is a 'PmExprCon' or 'PmExprLit' -trySolve:: Name -> PmExpr -> TmState -> Maybe TmState -trySolve x e _tms@TmS{ tm_pos = pos, tm_neg = neg } - | ASSERT( isFlexible _tms x ) - ASSERT( _is_whnf e ) - isRefutable x e neg - = Nothing - | otherwise - = Just (TmS (extendNameEnv pos x e) (delFromDNameEnv neg x)) - where - _is_whnf PmExprCon{} = True - _is_whnf PmExprLit{} = True - _is_whnf _ = False - --- | When we know that a variable is fresh, we do not actually have to --- check whether anything changes, we know that nothing does. Hence, --- @extendSubst@ simply extends the substitution, unlike what --- 'extendSubstAndSolve' does. -extendSubst :: Id -> PmExpr -> TmState -> TmState -extendSubst y e solver_state@TmS{ tm_pos = pos } - | isNotPmExprOther simpl_e - = solver_state { tm_pos = extendNameEnv pos x simpl_e } - | otherwise = solver_state - where - x = idName y - simpl_e = exprDeepLookup pos e - --- | Apply an (un-flattened) substitution to a variable and return its --- representative in the triangular substitution @env@ and the completely --- substituted expression. The latter may just be the representative wrapped --- with 'PmExprVar' if we haven't found a solution for it yet. -varDeepLookup :: TmVarCtEnv -> Name -> (Name, PmExpr) -varDeepLookup env x = case lookupNameEnv env x of - Just (PmExprVar y) -> varDeepLookup env y - Just e -> (x, exprDeepLookup env e) -- go deeper - Nothing -> (x, PmExprVar x) -- terminal -{-# INLINE varDeepLookup #-} - --- | Apply an (un-flattened) substitution to an expression. -exprDeepLookup :: TmVarCtEnv -> PmExpr -> PmExpr -exprDeepLookup env (PmExprVar x) = snd (varDeepLookup env x) -exprDeepLookup env (PmExprCon c es) = PmExprCon c (map (exprDeepLookup env) es) -exprDeepLookup _ other_expr = other_expr -- PmExprLit, PmExprOther - --- | External interface to the term oracle. -tmOracle :: TmState -> [TmVarCt] -> Maybe TmState -tmOracle tm_state eqs = foldlM solveOneEq tm_state eqs - --- | Type of a PmLit -pmLitType :: PmLit -> Type -- should be in PmExpr but gives cyclic imports :( -pmLitType (PmSLit lit) = hsLitType lit -pmLitType (PmOLit _ lit) = overLitType lit - -{- Note [Refutable shapes] -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Consider a pattern match like - - foo x - | 0 <- x = 42 - | 0 <- x = 43 - | 1 <- x = 44 - | otherwise = 45 - -This will result in the following initial matching problem: - - PatVec: x (0 <- x) - ValVec: $tm_y - -Where the first line is the pattern vector and the second line is the value -vector abstraction. When we handle the first pattern guard in Check, it will be -desugared to a match of the form - - PatVec: x 0 - ValVec: $tm_y x - -In LitVar, this will split the value vector abstraction for `x` into a positive -`PmLit 0` and a negative `PmLit x [0]` value abstraction. While the former is -immediately matched against the pattern vector, the latter (vector value -abstraction `~[0] $tm_y`) is completely uncovered by the clause. - -`pmcheck` proceeds by *discarding* the the value vector abstraction involving -the guard to accomodate for the desugaring. But this also discards the valuable -information that `x` certainly is not the literal 0! Consequently, we wouldn't -be able to report the second clause as redundant. - -That's a typical example of why we need the term oracle, and in this specific -case, the ability to encode that `x` certainly is not the literal 0. Now the -term oracle can immediately refute the constraint `x ~ 0` generated by the -second clause and report the clause as redundant. After the third clause, the -set of such *refutable* literals is again extended to `[0, 1]`. - -In general, we want to store a set of refutable shapes (`PmAltCon`) for each -variable. That's the purpose of the `PmRefutEnv`. `addSolveRefutableAltCon` will -add such a refutable mapping to the `PmRefutEnv` in the term oracles state and -check if causes any immediate contradiction. Whenever we record a solution in -the substitution via `extendSubstAndSolve`, the refutable environment is checked -for any matching refutable `PmAltCon`. --} diff --git a/compiler/ghc.cabal.in b/compiler/ghc.cabal.in index 7946e233c4..884006b487 100644 --- a/compiler/ghc.cabal.in +++ b/compiler/ghc.cabal.in @@ -336,7 +336,7 @@ Library PprCore PmExpr PmPpr - TmOracle + PmOracle Check Coverage Desugar @@ -572,7 +572,6 @@ Library IOEnv Json ListSetOps - ListT Maybes MonadUtils OrdList diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs index 5d56d33d53..0a2db75a2d 100644 --- a/compiler/typecheck/TcRnTypes.hs +++ b/compiler/typecheck/TcRnTypes.hs @@ -167,7 +167,7 @@ import TcType import Annotations import InstEnv import FamInstEnv -import PmExpr +import {-# SOURCE #-} PmOracle (Delta) import IOEnv import RdrName import Name @@ -390,10 +390,9 @@ data DsLclEnv = DsLclEnv { dsl_loc :: RealSrcSpan, -- To put in pattern-matching error msgs -- See Note [Note [Type and Term Equality Propagation] in Check.hs - -- These two fields are augmented as we walk inwards, - -- through each patttern match in turn - dsl_dicts :: Bag EvVar, -- Constraints from GADT pattern-matching - dsl_tm_cs :: Bag TmVarCt, -- Constraints form term-level pattern matching + -- The oracle state Delta is augmented as we walk inwards, + -- through each pattern match in turn + dsl_delta :: Delta, dsl_pm_iter :: IORef Int -- Number of iterations for pmcheck so far -- We fail if this gets too big diff --git a/compiler/typecheck/TcSimplify.hs b/compiler/typecheck/TcSimplify.hs index 48c7305669..18f2c505d9 100644 --- a/compiler/typecheck/TcSimplify.hs +++ b/compiler/typecheck/TcSimplify.hs @@ -688,6 +688,8 @@ constraints or to decide if a particular set of constraints is satisfiable, the purpose of tcNormalise is to take a type, plus some local constraints, and normalise the type as much as possible with respect to those constraints. +It does *not* reduce type or data family applications or look through newtypes. + Why is this useful? As one example, when coverage-checking an EmptyCase expression, it's possible that the type of the scrutinee will only reduce if some local equalities are solved for. See "Wrinkle: Local equalities" diff --git a/compiler/utils/Binary.hs b/compiler/utils/Binary.hs index 7a3b7a1472..164549fe58 100644 --- a/compiler/utils/Binary.hs +++ b/compiler/utils/Binary.hs @@ -935,7 +935,6 @@ putTypeRep bh (Fun arg res) = do put_ bh (3 :: Word8) putTypeRep bh arg putTypeRep bh res -putTypeRep _ _ = fail "Binary.putTypeRep: Impossible" getSomeTypeRep :: BinHandle -> IO SomeTypeRep getSomeTypeRep bh = do diff --git a/compiler/utils/ListSetOps.hs b/compiler/utils/ListSetOps.hs index 8078d5603e..a8b717df00 100644 --- a/compiler/utils/ListSetOps.hs +++ b/compiler/utils/ListSetOps.hs @@ -14,7 +14,7 @@ module ListSetOps ( Assoc, assoc, assocMaybe, assocUsing, assocDefault, assocDefaultUsing, -- Duplicate handling - hasNoDups, removeDups, findDupsEq, insertNoDup, + hasNoDups, removeDups, findDupsEq, equivClasses, -- Indexing @@ -178,10 +178,3 @@ findDupsEq _ [] = [] findDupsEq eq (x:xs) | null eq_xs = findDupsEq eq xs | otherwise = (x :| eq_xs) : findDupsEq eq neq_xs where (eq_xs, neq_xs) = partition (eq x) xs - --- | \( O(n) \). @'insertNoDup' x xs@ treats @xs@ as a set, inserting @x@ only --- when an equal element couldn't be found in @xs@. -insertNoDup :: (Eq a) => a -> [a] -> [a] -insertNoDup x set - | elem x set = set - | otherwise = x:set diff --git a/compiler/utils/ListT.hs b/compiler/utils/ListT.hs deleted file mode 100644 index 66e52ed9f4..0000000000 --- a/compiler/utils/ListT.hs +++ /dev/null @@ -1,79 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE Rank2Types #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} - -------------------------------------------------------------------------- --- | --- Module : Control.Monad.Logic --- Copyright : (c) Dan Doel --- License : BSD3 --- --- Maintainer : dan.doel@gmail.com --- Stability : experimental --- Portability : non-portable (multi-parameter type classes) --- --- A backtracking, logic programming monad. --- --- Adapted from the paper --- /Backtracking, Interleaving, and Terminating --- Monad Transformers/, by --- Oleg Kiselyov, Chung-chieh Shan, Daniel P. Friedman, Amr Sabry --- (<http://www.cs.rutgers.edu/~ccshan/logicprog/ListT-icfp2005.pdf>). -------------------------------------------------------------------------- - -module ListT ( - ListT(..), - runListT, - select, - fold - ) where - -import GhcPrelude - -import Control.Applicative - -import Control.Monad -import Control.Monad.Fail as MonadFail - -------------------------------------------------------------------------- --- | A monad transformer for performing backtracking computations --- layered over another monad 'm' -newtype ListT m a = - ListT { unListT :: forall r. (a -> m r -> m r) -> m r -> m r } - deriving (Functor) - -select :: Monad m => [a] -> ListT m a -select xs = foldr (<|>) mzero (map pure xs) - -fold :: ListT m a -> (a -> m r -> m r) -> m r -> m r -fold = runListT - -------------------------------------------------------------------------- --- | Runs a ListT computation with the specified initial success and --- failure continuations. -runListT :: ListT m a -> (a -> m r -> m r) -> m r -> m r -runListT = unListT - -instance Applicative (ListT f) where - pure a = ListT $ \sk fk -> sk a fk - f <*> a = ListT $ \sk fk -> unListT f (\g fk' -> unListT a (sk . g) fk') fk - -instance Alternative (ListT f) where - empty = ListT $ \_ fk -> fk - f1 <|> f2 = ListT $ \sk fk -> unListT f1 sk (unListT f2 sk fk) - -instance Monad (ListT m) where - m >>= f = ListT $ \sk fk -> unListT m (\a fk' -> unListT (f a) sk fk') fk -#if !MIN_VERSION_base(4,13,0) - fail = MonadFail.fail -#endif - -instance MonadFail.MonadFail (ListT m) where - fail _ = ListT $ \_ fk -> fk - -instance MonadPlus (ListT m) where - mzero = ListT $ \_ fk -> fk - m1 `mplus` m2 = ListT $ \sk fk -> unListT m1 sk (unListT m2 sk fk) diff --git a/compiler/utils/Outputable.hs b/compiler/utils/Outputable.hs index a5306faaa4..cd3e2a5f5b 100644 --- a/compiler/utils/Outputable.hs +++ b/compiler/utils/Outputable.hs @@ -1205,7 +1205,7 @@ pprTraceM str doc = pprTrace str doc (pure ()) -- | @pprTraceWith desc f x@ is equivalent to @pprTrace desc (f x) x@. -- This allows you to print details from the returned value as well as from -- ambient variables. -pprTraceWith :: Outputable a => String -> (a -> SDoc) -> a -> a +pprTraceWith :: String -> (a -> SDoc) -> a -> a pprTraceWith desc f x = pprTrace desc (f x) x -- | @pprTraceIt desc x@ is equivalent to @pprTrace desc (ppr x) x@ diff --git a/compiler/utils/Util.hs b/compiler/utils/Util.hs index 51812cca75..d6cea5ca8d 100644 --- a/compiler/utils/Util.hs +++ b/compiler/utils/Util.hs @@ -14,6 +14,9 @@ module Util ( ghciTablesNextToCode, isWindowsHost, isDarwinHost, + -- * Miscellaneous higher-order functions + applyWhen, nTimes, + -- * General list processing zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal, zipLazy, stretchZipWith, zipWithAndUnzip, zipAndUnzip, @@ -57,9 +60,6 @@ module Util ( takeList, dropList, splitAtList, split, dropTail, capitalise, - -- * For loop - nTimes, - -- * Sorting sortWith, minWith, nubSort, ordNub, @@ -222,12 +222,17 @@ isDarwinHost = False {- ************************************************************************ * * -\subsection{A for loop} +\subsection{Miscellaneous higher-order functions} * * ************************************************************************ -} --- | Compose a function with itself n times. (nth rather than twice) +-- | Apply a function iff some condition is met. +applyWhen :: Bool -> (a -> a) -> a -> a +applyWhen True f x = f x +applyWhen _ _ x = x + +-- | A for loop: Compose a function with itself n times. (nth rather than twice) nTimes :: Int -> (a -> a) -> (a -> a) nTimes 0 _ = id nTimes 1 f = f diff --git a/docs/users_guide/glasgow_exts.rst b/docs/users_guide/glasgow_exts.rst index 3c951466c9..5cf3c4b9cc 100644 --- a/docs/users_guide/glasgow_exts.rst +++ b/docs/users_guide/glasgow_exts.rst @@ -15541,49 +15541,6 @@ the user must provide a type signature. :: foo :: [a] -> Int foo T = 5 -.. _multiple-complete-pragmas: - -Disambiguating between multiple ``COMPLETE`` pragmas ----------------------------------------------------- - -What should happen if there are multiple ``COMPLETE`` sets that apply to a -single set of patterns? Consider this example: :: - - data T = MkT1 | MkT2 | MkT2Internal - {-# COMPLETE MkT1, MkT2 #-} - {-# COMPLETE MkT1, MkT2Internal #-} - - f :: T -> Bool - f MkT1 = True - f MkT2 = False - -Which ``COMPLETE`` pragma should be used when checking the coverage of the -patterns in ``f``? If we pick the ``COMPLETE`` set that covers ``MkT1`` and -``MkT2``, then ``f`` is exhaustive, but if we pick the other ``COMPLETE`` set -that covers ``MkT1`` and ``MkT2Internal``, then ``f`` is *not* exhaustive, -since it fails to match ``MkT2Internal``. An intuitive way to solve this -dilemma is to recognize that picking the former ``COMPLETE`` set produces the -fewest number of uncovered pattern clauses, and thus is the better choice. - -GHC disambiguates between multiple ``COMPLETE`` sets based on this rationale. -To make things more formal, when the pattern-match checker requests a set of -constructors for some data type constructor ``T``, the checker returns: - -* The original set of data constructors for ``T`` -* Any ``COMPLETE`` sets of type ``T`` - -GHC then checks for pattern coverage using each of these sets. If any of these -sets passes the pattern coverage checker with no warnings, then we are done. If -each set produces at least one warning, then GHC must pick one of the sets of -warnings depending on how good the results are. The results are prioritized in -this order: - -1. Fewest uncovered clauses -2. Fewest redundant clauses -3. Fewest inaccessible clauses -4. Whether the match comes from the original set of data constructors or from a - ``COMPLETE`` pragma (prioritizing the former over the latter) - .. _overlap-pragma: ``OVERLAPPING``, ``OVERLAPPABLE``, ``OVERLAPS``, and ``INCOHERENT`` pragmas diff --git a/libraries/binary b/libraries/binary -Subproject e26b50021c9fdc3d82cd6ed2ee2edb819c7d048 +Subproject dfaf780596328c9184758452b78288e8f405fcc diff --git a/testsuite/tests/deSugar/should_compile/T14546d.hs b/testsuite/tests/deSugar/should_compile/T14546d.hs new file mode 100644 index 0000000000..099e64727a --- /dev/null +++ b/testsuite/tests/deSugar/should_compile/T14546d.hs @@ -0,0 +1,8 @@ +data D = D deriving Eq + +instance Num D where + fromInteger _ = D + +main = do + case 3 :: D of + 1 -> putStrLn "A" diff --git a/testsuite/tests/deSugar/should_compile/T14546d.stderr b/testsuite/tests/deSugar/should_compile/T14546d.stderr new file mode 100644 index 0000000000..db5b9ca285 --- /dev/null +++ b/testsuite/tests/deSugar/should_compile/T14546d.stderr @@ -0,0 +1,9 @@ + +T14546d.hs:3:10: warning: [-Wmissing-methods (in -Wdefault)] + • No explicit implementation for + ‘+’, ‘*’, ‘abs’, ‘signum’, and (either ‘negate’ or ‘-’) + • In the instance declaration for ‘Num D’ + +T14546d.hs:7:5: warning: [-Wincomplete-patterns (in -Wextra)] + Pattern match(es) are non-exhaustive + In a case alternative: Patterns not matched: 3 diff --git a/testsuite/tests/deSugar/should_compile/T14773b.stderr b/testsuite/tests/deSugar/should_compile/T14773b.stderr index 557b10b8f0..b204b4f485 100644 --- a/testsuite/tests/deSugar/should_compile/T14773b.stderr +++ b/testsuite/tests/deSugar/should_compile/T14773b.stderr @@ -1,4 +1,8 @@ +T14773b.hs:4:10: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In a pattern binding guards: = ... + T14773b.hs:4:10: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In a pattern binding guards: diff --git a/testsuite/tests/deSugar/should_compile/all.T b/testsuite/tests/deSugar/should_compile/all.T index 5369b3f473..21cf1b058d 100644 --- a/testsuite/tests/deSugar/should_compile/all.T +++ b/testsuite/tests/deSugar/should_compile/all.T @@ -102,6 +102,7 @@ test('T14135', normal, compile, ['']) test('T14546a', normal, compile, ['-Wincomplete-patterns']) test('T14546b', normal, compile, ['-Wincomplete-patterns']) test('T14546c', normal, compile, ['-Wincomplete-patterns']) +test('T14546d', normal, compile, ['-Wincomplete-patterns']) test('T14547', normal, compile, ['-Wincomplete-patterns']) test('T14773a', normal, compile, ['-Wincomplete-patterns']) test('T14773b', normal, compile, ['-Wincomplete-patterns']) diff --git a/testsuite/tests/dependent/should_compile/KindEqualities.stderr b/testsuite/tests/dependent/should_compile/KindEqualities.stderr index ad9562eae8..684c1380aa 100644 --- a/testsuite/tests/dependent/should_compile/KindEqualities.stderr +++ b/testsuite/tests/dependent/should_compile/KindEqualities.stderr @@ -2,4 +2,5 @@ KindEqualities.hs:25:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘zero’: - Patterns not matched: (TyApp (TyApp _ _) _) + Patterns not matched: + (TyApp (TyApp p _) _) where p is not one of {TyInt} diff --git a/testsuite/tests/driver/T8101.stderr b/testsuite/tests/driver/T8101.stderr index 9f57360448..a486f965e3 100644 --- a/testsuite/tests/driver/T8101.stderr +++ b/testsuite/tests/driver/T8101.stderr @@ -1,3 +1,4 @@ + T8101.hs:7:9: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In a case alternative: diff --git a/testsuite/tests/haddock/should_compile_noflag_haddock/haddockSimplUtilsBug.stderr b/testsuite/tests/haddock/should_compile_noflag_haddock/haddockSimplUtilsBug.stderr new file mode 100644 index 0000000000..2d3393b249 --- /dev/null +++ b/testsuite/tests/haddock/should_compile_noflag_haddock/haddockSimplUtilsBug.stderr @@ -0,0 +1,4 @@ + +haddockSimplUtilsBug.hs:28:7: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In a case alternative: _ -> ... diff --git a/testsuite/tests/parser/should_compile/read044.stderr b/testsuite/tests/parser/should_compile/read044.stderr new file mode 100644 index 0000000000..d459248082 --- /dev/null +++ b/testsuite/tests/parser/should_compile/read044.stderr @@ -0,0 +1,4 @@ + +read044.hs:5:13: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In a case alternative: False -> ... diff --git a/testsuite/tests/patsyn/should_run/all.T b/testsuite/tests/patsyn/should_run/all.T index 57bc41fc73..90f577174e 100644 --- a/testsuite/tests/patsyn/should_run/all.T +++ b/testsuite/tests/patsyn/should_run/all.T @@ -13,7 +13,7 @@ test('unboxed-wrapper', normal, compile_and_run, ['']) test('records-run', normal, compile_and_run, ['']) test('ghci', just_ghci, ghci_script, ['ghci.script']) test('T11985', just_ghci, ghci_script, ['T11985.script']) -test('T11224', normal, compile_and_run, ['']) +test('T11224', normal, compile_and_run, ['-Wincomplete-patterns -Woverlapping-patterns']) # we omit profasm/profthreaded because it doesn't bring much to the table but # introduces its share of complexity, as the test as it is fails with # profasm: diff --git a/testsuite/tests/pmcheck/complete_sigs/T13021.hs b/testsuite/tests/pmcheck/complete_sigs/T13021.hs new file mode 100644 index 0000000000..cb2a725a98 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13021.hs @@ -0,0 +1,11 @@ +{-# OPTIONS_GHC -Woverlapping-patterns -Wincomplete-patterns #-} + +module Lib where + +data T = A | B | C + +{-# COMPLETE B #-} + +foo :: T -> () +foo A = () +foo B = () diff --git a/testsuite/tests/pmcheck/complete_sigs/T13363a.hs b/testsuite/tests/pmcheck/complete_sigs/T13363a.hs new file mode 100644 index 0000000000..1d614b7d05 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13363a.hs @@ -0,0 +1,16 @@ +{-# OPTIONS_GHC -Wincomplete-patterns -Woverlapping-patterns #-} +{-# LANGUAGE PatternSynonyms #-} + +module Lib where + +data Boolean = F | T + deriving Eq + +pattern TooGoodToBeTrue :: Boolean +pattern TooGoodToBeTrue = T +{-# COMPLETE F, TooGoodToBeTrue #-} + +catchAll :: Boolean -> Int +catchAll F = 0 +catchAll TooGoodToBeTrue = 1 +catchAll _ = error "impossible" diff --git a/testsuite/tests/pmcheck/complete_sigs/T13363a.stderr b/testsuite/tests/pmcheck/complete_sigs/T13363a.stderr new file mode 100644 index 0000000000..a91b02806a --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13363a.stderr @@ -0,0 +1,4 @@ + +T13363a.hs:16:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘catchAll’: catchAll _ = ... diff --git a/testsuite/tests/pmcheck/complete_sigs/T13363b.hs b/testsuite/tests/pmcheck/complete_sigs/T13363b.hs new file mode 100644 index 0000000000..0ef350e491 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13363b.hs @@ -0,0 +1,17 @@ +{-# OPTIONS_GHC -Wincomplete-patterns -Woverlapping-patterns #-} +{-# LANGUAGE PatternSynonyms #-} + +module Lib where + +data T = A | B | C + deriving Eq + +pattern BC :: T +pattern BC = C + +{-# COMPLETE A, BC #-} + +f A = 1 +f B = 2 +f BC = 3 +f _ = error "impossible" diff --git a/testsuite/tests/pmcheck/complete_sigs/T13363b.stderr b/testsuite/tests/pmcheck/complete_sigs/T13363b.stderr new file mode 100644 index 0000000000..541c084be1 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13363b.stderr @@ -0,0 +1,4 @@ + +T13363b.hs:17:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘f’: f _ = ... diff --git a/testsuite/tests/pmcheck/complete_sigs/T13717.hs b/testsuite/tests/pmcheck/complete_sigs/T13717.hs new file mode 100644 index 0000000000..e9460371c8 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13717.hs @@ -0,0 +1,41 @@ +{-# OPTIONS_GHC -Wincomplete-patterns #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE EmptyCase #-} + +module Fin (Nat (..), Fin (FZ, FS)) where +import Numeric.Natural +import Unsafe.Coerce + +data Nat = Z | S Nat + +-- Fin *must* be exported abstractly (or placed in an Unsafe +-- module) to maintain type safety. +newtype Fin (n :: Nat) = Fin Natural + +data FinView n where + VZ :: FinView ('S n) + VS :: !(Fin n) -> FinView ('S n) + +viewFin :: Fin n -> FinView n +viewFin (Fin 0) = unsafeCoerce VZ +viewFin (Fin n) = unsafeCoerce (VS (Fin (n - 1))) + +pattern FZ :: () => n ~ 'S m => Fin n +pattern FZ <- (viewFin -> VZ) where + FZ = Fin 0 + +pattern FS :: () => n ~ 'S m => Fin m -> Fin n +pattern FS m <- (viewFin -> VS m) where + FS (Fin m) = Fin (1 + m) + +{-# COMPLETE FZ, FS #-} + +finZAbsurd :: Fin 'Z -> a +finZAbsurd x = case x of + diff --git a/testsuite/tests/pmcheck/complete_sigs/T13964.hs b/testsuite/tests/pmcheck/complete_sigs/T13964.hs new file mode 100644 index 0000000000..36a87a9a25 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13964.hs @@ -0,0 +1,20 @@ +{-# OPTIONS_GHC -Wincomplete-patterns #-} + +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ViewPatterns #-} + +module Bug (Boolean(F, TooGoodToBeTrue), catchAll) where + +data Boolean = F | T + deriving Eq + +pattern TooGoodToBeTrue :: Boolean +pattern TooGoodToBeTrue <- ((== T) -> True) + where + TooGoodToBeTrue = T +{-# COMPLETE F, TooGoodToBeTrue #-} + +catchAll :: Boolean -> Int +catchAll F = 0 +-- catchAll TooGoodToBeTrue = 1 + diff --git a/testsuite/tests/pmcheck/complete_sigs/T13964.stderr b/testsuite/tests/pmcheck/complete_sigs/T13964.stderr new file mode 100644 index 0000000000..606756a783 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13964.stderr @@ -0,0 +1,4 @@ + +T13964.hs:18:1: warning: [-Wincomplete-patterns (in -Wextra)] + Pattern match(es) are non-exhaustive + In an equation for ‘catchAll’: Patterns not matched: T diff --git a/testsuite/tests/pmcheck/complete_sigs/T13965.hs b/testsuite/tests/pmcheck/complete_sigs/T13965.hs new file mode 100644 index 0000000000..ac18dad115 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13965.hs @@ -0,0 +1,20 @@ +{-# OPTIONS_GHC -Woverlapping-patterns #-} + +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ViewPatterns #-} +module Bug (Boolean(F, TooGoodToBeTrue), catchAll) where + +data Boolean = F | T + deriving Eq + +pattern TooGoodToBeTrue :: Boolean +pattern TooGoodToBeTrue <- ((== T) -> True) + where + TooGoodToBeTrue = T +{-# COMPLETE F, TooGoodToBeTrue #-} + +catchAll :: Boolean -> Int +catchAll F = 0 +catchAll TooGoodToBeTrue = 1 +catchAll F = 2 + diff --git a/testsuite/tests/pmcheck/complete_sigs/T13965.stderr b/testsuite/tests/pmcheck/complete_sigs/T13965.stderr new file mode 100644 index 0000000000..78aaa9490a --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T13965.stderr @@ -0,0 +1,4 @@ + +T13965.hs:19:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘catchAll’: catchAll F = ... diff --git a/testsuite/tests/pmcheck/complete_sigs/T14059b.hs b/testsuite/tests/pmcheck/complete_sigs/T14059b.hs new file mode 100644 index 0000000000..52f3593ecd --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T14059b.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeInType #-} +{-# LANGUAGE TypeOperators #-} +{-# OPTIONS_GHC -Wincomplete-patterns #-} +module Bug where + +data family Sing (a :: k) + +data instance Sing (z :: Bool) where + SFalse :: Sing False + STrue :: Sing True + +pattern STooGoodToBeTrue :: forall (z :: Bool). () + => z ~ True + => Sing z +pattern STooGoodToBeTrue = STrue +{-# COMPLETE SFalse, STooGoodToBeTrue #-} + +wibble :: Sing (z :: Bool) -> Bool +wibble STrue = True + +wobble :: Sing (z :: Bool) -> Bool +wobble STooGoodToBeTrue = True + diff --git a/testsuite/tests/pmcheck/complete_sigs/T14059b.stderr b/testsuite/tests/pmcheck/complete_sigs/T14059b.stderr new file mode 100644 index 0000000000..8c8569c638 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T14059b.stderr @@ -0,0 +1,10 @@ + +T14059b.hs:23:1: warning: [-Wincomplete-patterns (in -Wextra)] + Pattern match(es) are non-exhaustive + In an equation for ‘wibble’: + Patterns not matched: p where p is not one of {STrue} + +T14059b.hs:26:1: warning: [-Wincomplete-patterns (in -Wextra)] + Pattern match(es) are non-exhaustive + In an equation for ‘wobble’: + Patterns not matched: p where p is not one of {STooGoodToBeTrue} diff --git a/testsuite/tests/pmcheck/complete_sigs/T14851.hs b/testsuite/tests/pmcheck/complete_sigs/T14851.hs new file mode 100644 index 0000000000..d5baac58a2 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T14851.hs @@ -0,0 +1,21 @@ +{-# OPTIONS_GHC -Woverlapping-patterns #-} + +{-# LANGUAGE PatternSynonyms, ViewPatterns #-} + +module Bug where + +import Type.Reflection + +pattern X arg <- (checkFun -> arg) + +checkFun :: TypeRep fun -> a +checkFun = undefined + +f x = case (x, True) of + (X _, _) -> 5 + _ -> 6 + +g x = case x of + (X _) -> 5 + _ -> 6 + diff --git a/testsuite/tests/pmcheck/complete_sigs/T17149.hs b/testsuite/tests/pmcheck/complete_sigs/T17149.hs new file mode 100644 index 0000000000..ba68154f3c --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/T17149.hs @@ -0,0 +1,21 @@ +{-# OPTIONS_GHC -Wincomplete-patterns #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +module Bug where + +class Member a b where + prj :: b -> Maybe a + +pattern P :: Member a b => a -> b +pattern P a <- (prj -> Just a) + +{-# COMPLETE P :: Bool #-} + +-- | Trying to instantiate P with 0 type arguments doesn't work, it takes 2. +-- This seemingly unrelated fact, only relevant through the COMPLETE set, may +-- not lead the compiler to crash or do shady stuff. +fun :: Bool -> () +fun True = () +fun _ = () diff --git a/testsuite/tests/pmcheck/complete_sigs/all.T b/testsuite/tests/pmcheck/complete_sigs/all.T index d58c182f8e..7e70f3aa10 100644 --- a/testsuite/tests/pmcheck/complete_sigs/all.T +++ b/testsuite/tests/pmcheck/complete_sigs/all.T @@ -13,5 +13,14 @@ test('completesig12', normal, compile, ['']) test('completesig13', normal, compile, ['']) test('completesig14', normal, compile, ['']) test('completesig15', normal, compile_fail, ['']) +test('T13021', normal, compile, ['']) +test('T13363a', normal, compile, ['']) +test('T13363b', normal, compile, ['']) +test('T13717', expect_broken('13717'), compile, ['']) +test('T13964', normal, compile, ['']) +test('T13965', normal, compile, ['']) test('T14059a', normal, compile, ['']) -test('T14253', expect_broken(14253), compile, ['']) +test('T14059b', expect_broken('14059'), compile, ['']) +test('T14253', normal, compile, ['']) +test('T14851', normal, compile, ['']) +test('T17149', normal, compile, ['']) diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig01.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig01.stderr new file mode 100644 index 0000000000..63cff81b5b --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/completesig01.stderr @@ -0,0 +1,4 @@ + +completesig01.hs:20:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘b’: b C = ... diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr index 25b24fd836..6da127a674 100644 --- a/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr +++ b/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr @@ -1,4 +1,4 @@ completesig02.hs:10:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive - In an equation for ‘a’: Patterns not matched: _ + In an equation for ‘a’: Patterns not matched: () diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr index 50bc9bfebf..66a7604ca9 100644 --- a/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr +++ b/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr @@ -18,8 +18,8 @@ completesig06.hs:23:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘m4’: Patterns not matched: - B D A D + B D completesig06.hs:29:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr index bf5edb9205..b8e56892c9 100644 --- a/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr +++ b/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr @@ -1,4 +1,12 @@ +completesig07.hs:14:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m1’: m1 A = ... + +completesig07.hs:20:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m2’: m2 A D = ... + completesig07.hs:23:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘m3’: diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig08.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig08.stderr new file mode 100644 index 0000000000..2c7d6658e7 --- /dev/null +++ b/testsuite/tests/pmcheck/complete_sigs/completesig08.stderr @@ -0,0 +1,20 @@ + +completesig08.hs:15:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m1’: m1 C = ... + +completesig08.hs:20:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m2’: m2 C D = ... + +completesig08.hs:25:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m3’: m3 A E = ... + +completesig08.hs:26:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m3’: m3 A F = ... + +completesig08.hs:30:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m4’: m4 E = ... diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr index 3d97bb47d7..36b367068c 100644 --- a/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr +++ b/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr @@ -1,4 +1,12 @@ +completesig10.hs:15:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m1’: m1 C = ... + +completesig10.hs:16:1: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In an equation for ‘m1’: m1 D = ... + completesig10.hs:20:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘m2’: Patterns not matched: A diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr index 8107071008..d7bf2f3079 100644 --- a/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr +++ b/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr @@ -1,4 +1,4 @@ completesig11.hs:14:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive - In an equation for ‘m1’: Patterns not matched: BS + In an equation for ‘m1’: Patterns not matched: B diff --git a/testsuite/tests/pmcheck/should_compile/CaseOfKnownCon.hs b/testsuite/tests/pmcheck/should_compile/CaseOfKnownCon.hs new file mode 100644 index 0000000000..0933baae96 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/CaseOfKnownCon.hs @@ -0,0 +1,8 @@ +-- | Ideally, we want the pattern match in `f` to be discovered as exhaustive, +-- with a redundant match on the second clause. +module Lib where + +f :: () +f = case (True, False) of + (True, False) -> () + (True, True) -> () diff --git a/testsuite/tests/pmcheck/should_compile/CaseOfKnownCon.stderr b/testsuite/tests/pmcheck/should_compile/CaseOfKnownCon.stderr new file mode 100644 index 0000000000..1f09323956 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/CaseOfKnownCon.stderr @@ -0,0 +1,4 @@ + +CaseOfKnownCon.hs:8:3: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In a case alternative: (True, True) -> ... diff --git a/testsuite/tests/pmcheck/should_compile/CyclicSubst.hs b/testsuite/tests/pmcheck/should_compile/CyclicSubst.hs index fde022c5cb..3f37b66b54 100644 --- a/testsuite/tests/pmcheck/should_compile/CyclicSubst.hs +++ b/testsuite/tests/pmcheck/should_compile/CyclicSubst.hs @@ -1,5 +1,5 @@ -- | The following match demonstrates why we need to detect cyclic solutions --- when extending 'TmOracle.tm_pos'. +-- when extending 'PmOracle.tm_pos'. -- -- TLDR; solving @x :-> y@ where @x@ is the representative of @y@'s equivalence -- class can easily lead to a cycle in the substitution. diff --git a/testsuite/tests/pmcheck/should_compile/EmptyCase005.stderr b/testsuite/tests/pmcheck/should_compile/EmptyCase005.stderr index 53be507400..8cedcddaf5 100644 --- a/testsuite/tests/pmcheck/should_compile/EmptyCase005.stderr +++ b/testsuite/tests/pmcheck/should_compile/EmptyCase005.stderr @@ -1,3 +1,4 @@ + EmptyCase005.hs:24:8: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In a case alternative: Patterns not matched: (Void3 _) diff --git a/testsuite/tests/pmcheck/should_compile/PmExprVars.hs b/testsuite/tests/pmcheck/should_compile/PmExprVars.hs index 7b17cd5b66..fc95d43246 100644 --- a/testsuite/tests/pmcheck/should_compile/PmExprVars.hs +++ b/testsuite/tests/pmcheck/should_compile/PmExprVars.hs @@ -1,20 +1,5 @@ module PmExprVars where --- | Demonstrates why we can't lower constructors as flexible meta variables. --- If we did, we'd get a warning that cases 1 and 2 were redundant, implying --- cases 0 and 3 are not. Arguably this might be better than not warning at --- all, but it's very surprising having to supply the third case but not the --- first two cases. And it's probably buggy somwhere else. Delete this when we --- detect that all but the last case is redundant. -consAreRigid :: Int -consAreRigid = case False of - False -> case False of - False -> 0 - True -> 1 - True -> case False of - False -> 2 - True -> 3 - data D a = A | B class C a where diff --git a/testsuite/tests/pmcheck/should_compile/T11336b.hs b/testsuite/tests/pmcheck/should_compile/T11336b.hs new file mode 100644 index 0000000000..37734eabb6 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/T11336b.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ViewPatterns #-} + +-- | Derived from T11336. Note that the pattern synonym makes it so that the +-- pattern match checker can't see any complete sets: It only sees @s a@ +-- instead of @Proxy a@ for the match in 'PProxy'. +module Bug where + +import Data.Proxy + +class Prj s where + prj :: Proxy a -> s a + +instance Prj Proxy where + prj = id + +pattern PProxy :: Prj s => s a -> Proxy a +pattern PProxy s <- (prj -> s) + +-- | Although this is technically a complete match, the pattern match checker +-- can't in general look through the pattern synonym. So, it should warn that +-- some pattern wasn't matched. It should still flag the redundant second +-- clause, though! +fun :: Proxy a -> () +fun (PProxy Proxy) = () +fun (PProxy Proxy) = () diff --git a/testsuite/tests/pmcheck/should_compile/T11336b.stderr b/testsuite/tests/pmcheck/should_compile/T11336b.stderr new file mode 100644 index 0000000000..5d479c3756 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/T11336b.stderr @@ -0,0 +1,4 @@ + +T11336b.hs:25:1: warning: [-Wincomplete-patterns (in -Wextra)] + Pattern match(es) are non-exhaustive + In an equation for ‘fun’: Patterns not matched: _ diff --git a/testsuite/tests/pmcheck/should_compile/T15753c.hs b/testsuite/tests/pmcheck/should_compile/T15753c.hs new file mode 100644 index 0000000000..d04a889e00 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/T15753c.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE EmptyCase #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -Wincomplete-patterns #-} +module Bug where + +import Data.Kind (Type) +import Data.Type.Equality ((:~:)(..)) +import Data.Void (Void) + +data SBool :: Bool -> Type where + SFalse :: SBool False + STrue :: SBool True +data SUnit :: () -> Type where + SUnit :: SUnit '() + +type family IsUnit (u :: ()) :: Bool where + IsUnit '() = True + +sIsUnit :: SUnit u -> SBool (IsUnit u) +sIsUnit SUnit = STrue + +type family If (c :: Bool) (t :: Type) (f :: Type) :: Type where + If True t _ = t + If False _ f = f + +type family F (u1 :: ()) (u2 :: ()) :: Type where + F u1 u2 = + If (IsUnit u1) (Case u2) Int + +type family Case (u :: ()) :: Type where + Case '() = Int + +g1 :: F u1 u2 :~: Char + -> SUnit u1 -> SUnit u2 + -> Void +g1 Refl su1 su2 + | STrue <- sIsUnit su1 + = case su2 of {} + +g2 :: F u1 u2 :~: Char + -> SUnit u1 -> SUnit u2 + -> Void +g2 Refl su1 su2 + = case sIsUnit su1 of + STrue -> + case su2 of {} + diff --git a/testsuite/tests/pmcheck/should_compile/T15753d.hs b/testsuite/tests/pmcheck/should_compile/T15753d.hs new file mode 100644 index 0000000000..5935cf7d9d --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/T15753d.hs @@ -0,0 +1,95 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE EmptyCase #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -Wall -Wno-unticked-promoted-constructors #-} +module Bug where + +import Data.Kind +import Data.Type.Bool +import Data.Type.Equality ((:~:)(..)) +import Data.Void + +data family Sing :: forall k. k -> Type +data instance Sing :: Bool -> Type where + SFalse :: Sing False + STrue :: Sing True +data instance Sing :: forall a. [a] -> Type where + SNil :: Sing '[] + SCons :: Sing x -> Sing xs -> Sing (x:xs) +data instance Sing :: forall a b. (a, b) -> Type where + STuple2 :: Sing x -> Sing y -> Sing '(x, y) +newtype instance Sing (f :: k1 ~> k2) = + SLambda { (@@) :: forall t. Sing t -> Sing (f @@ t) } + +data TyFun :: Type -> Type -> Type +type a ~> b = TyFun a b -> Type +infixr 0 ~> +type family (f :: k1 ~> k2) @@ (x :: k1) :: k2 +infixl 9 @@ + +newtype Map k v = MkMap [(k, v)] +data instance Sing :: forall k v. Map k v -> Type where + SMkMap :: Sing x -> Sing (MkMap x) + +type family MapEmpty :: Map k v where + MapEmpty = MkMap '[] + +type family MapInsertWith (f :: v ~> v ~> v) (new_k :: k) (new_v :: v) (m :: Map k v) :: Map k v where + MapInsertWith _ new_k new_v (MkMap '[]) = MkMap '[ '(new_k, new_v)] + MapInsertWith f new_k new_v (MkMap ('(old_k,old_v):old_kvs)) = + If (old_k == new_k) + (MkMap ('(new_k,f @@ new_v @@ old_v):old_kvs)) + (Case (MapInsertWith f new_k new_v (MkMap old_kvs)) old_k old_v) + +type family Case (m :: Map k v) (old_k :: k) (old_v :: v) :: Map k v where + Case (MkMap kvs) old_k old_v = MkMap ('(old_k,old_v) : kvs) + +sMapInsertWith :: forall k v (f :: v ~> v ~> v) (new_k :: k) (new_v :: v) (m :: Map k v). + SEq k + => Sing f -> Sing new_k -> Sing new_v -> Sing m + -> Sing (MapInsertWith f new_k new_v m) +sMapInsertWith _ snew_k snew_v (SMkMap SNil) = SMkMap (STuple2 snew_k snew_v `SCons` SNil) +sMapInsertWith sf snew_k snew_v (SMkMap (STuple2 sold_k sold_v `SCons` sold_kvs)) = + case sold_k %== snew_k of + STrue -> SMkMap (STuple2 snew_k (sf @@ snew_v @@ sold_v) `SCons` sold_kvs) + SFalse -> + case sMapInsertWith sf snew_k snew_v (SMkMap sold_kvs) of + SMkMap skvs -> SMkMap (STuple2 sold_k sold_v `SCons` skvs) + +class PEq a where + type (x :: a) == (y :: a) :: Bool +class SEq a where + (%==) :: forall (x :: a) (y :: a). + Sing x -> Sing y -> Sing (x == y) + +mapInsertWithNonEmpty1 :: forall k v (f :: v ~> v ~> v) (old_k :: k) (old_v :: v) (old_kvs :: [(k,v)]) + (new_k :: k) (new_v :: v) (m :: Map k v). + SEq k + => Sing f -> Sing new_k -> Sing new_v -> Sing m + -> m :~: MkMap ('(old_k,old_v) : old_kvs) + -> MapInsertWith f new_k new_v m :~: MapEmpty + -> Void +mapInsertWithNonEmpty1 sf snew_k snew_v (SMkMap sm) Refl Refl + | STuple2 sold_k _ `SCons` sold_kvs <- sm + , SFalse <- sold_k %== snew_k + = case sMapInsertWith sf snew_k snew_v (SMkMap sold_kvs) of {} + +mapInsertWithNonEmpty2 :: forall k v (f :: v ~> v ~> v) (old_k :: k) (old_v :: v) (old_kvs :: [(k,v)]) + (new_k :: k) (new_v :: v) (m :: Map k v). + SEq k + => Sing f -> Sing new_k -> Sing new_v -> Sing m + -> m :~: MkMap ('(old_k,old_v) : old_kvs) + -> MapInsertWith f new_k new_v m :~: MapEmpty + -> Void +mapInsertWithNonEmpty2 sf snew_k snew_v (SMkMap sm) Refl Refl + | STuple2 sold_k _ `SCons` sold_kvs <- sm + = case sold_k %== snew_k of + SFalse -> + case sMapInsertWith sf snew_k snew_v (SMkMap sold_kvs) of {} + diff --git a/testsuite/tests/pmcheck/should_compile/T17096.hs b/testsuite/tests/pmcheck/should_compile/T17096.hs new file mode 100755 index 0000000000..5fc4e6e879 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/T17096.hs @@ -0,0 +1,319 @@ +{-# language PatternSynonyms #-} +-- Taken from the Dhall library +module T17096 where + +data Expr s a + = Const String + | Var Int + | Lam String (Expr s a) (Expr s a) + | Pi String (Expr s a) (Expr s a) + | App (Expr s a) (Expr s a) + | Let String (Maybe (Expr s a)) (Expr s a) (Expr s a) + | Annot (Expr s a) (Expr s a) + | Bool + | BoolLit Bool + | BoolAnd (Expr s a) (Expr s a) + | BoolOr (Expr s a) (Expr s a) + | BoolEQ (Expr s a) (Expr s a) + | BoolNE (Expr s a) (Expr s a) + | BoolIf (Expr s a) (Expr s a) (Expr s a) + | Natural + | NaturalLit Integer + | NaturalFold + | NaturalBuild + | NaturalIsZero + | NaturalEven + | NaturalOdd + | NaturalToInteger + | NaturalShow + | NaturalSubtract + | NaturalPlus (Expr s a) (Expr s a) + | NaturalTimes (Expr s a) (Expr s a) + | Integer + | IntegerLit Integer + | IntegerShow + | IntegerToDouble + | Double + | DoubleLit Double + | DoubleShow + | String + | StringLit String + | StringAppend (Expr s a) (Expr s a) + | StringShow + | List + | ListLit (Maybe (Expr s a)) [Expr s a] + | ListAppend (Expr s a) (Expr s a) + | ListBuild + | ListFold + | ListLength + | ListHead + | ListLast + | ListIndexed + | ListReverse + | Optional + | Some (Expr s a) + | None + | OptionalFold + | OptionalBuild + | Record [(String, Expr s a)] + | RecordLit [(String, Expr s a)] + | Union [(String, Maybe (Expr s a))] + | Combine (Expr s a) (Expr s a) + | CombineTypes (Expr s a) (Expr s a) + | Prefer (Expr s a) (Expr s a) + | Merge (Expr s a) (Expr s a) (Maybe (Expr s a)) + | ToMap (Expr s a) (Maybe (Expr s a)) + | Field (Expr s a) String + | Project (Expr s a) (Either [String] (Expr s a)) + | Assert (Expr s a) + | Equivalent (Expr s a) (Expr s a) + | Note s (Expr s a) + | ImportAlt (Expr s a) (Expr s a) + | Embed a + +isNormalized :: Eq a => Expr s a -> Bool +isNormalized = loop + where + loop e = case e of + Const _ -> True + Var _ -> True + Lam _ a b -> loop a && loop b + Pi _ a b -> loop a && loop b + App f a -> loop f && loop a && case App f a of + App (Lam _ _ _) _ -> False + App (App ListBuild _) (App (App ListFold _) _) -> False + App NaturalBuild (App NaturalFold _) -> False + App (App OptionalBuild _) (App (App OptionalFold _) _) -> False + App (App (App (App NaturalFold (NaturalLit _)) _) _) _ -> False + App NaturalFold (NaturalLit _) -> False + App NaturalBuild _ -> False + App NaturalIsZero (NaturalLit _) -> False + App NaturalEven (NaturalLit _) -> False + App NaturalOdd (NaturalLit _) -> False + App NaturalShow (NaturalLit _) -> False + App (App NaturalSubtract (NaturalLit _)) (NaturalLit _) -> False + App (App NaturalSubtract (NaturalLit 0)) _ -> False + App (App NaturalSubtract _) (NaturalLit 0) -> False + App (App NaturalSubtract x) y -> not (undefined x y) + App NaturalToInteger (NaturalLit _) -> False + App IntegerShow (IntegerLit _) -> False + App IntegerToDouble (IntegerLit _) -> False + App DoubleShow (DoubleLit _) -> False + App (App OptionalBuild _) _ -> False + App (App ListBuild _) _ -> False + App (App (App (App (App ListFold _) (ListLit _ _)) _) _) _ -> + False + App (App ListLength _) (ListLit _ _) -> False + App (App ListHead _) (ListLit _ _) -> False + App (App ListLast _) (ListLit _ _) -> False + App (App ListIndexed _) (ListLit _ _) -> False + App (App ListReverse _) (ListLit _ _) -> False + App (App (App (App (App OptionalFold _) (Some _)) _) _) _ -> + False + App (App (App (App (App OptionalFold _) (App None _)) _) _) _ -> + False + App StringShow (StringLit _) -> + False + _ -> True + Let _ _ _ _ -> False + Annot _ _ -> False + Bool -> True + BoolLit _ -> True + BoolAnd x y -> loop x && loop y && decide x y + where + decide (BoolLit _) _ = False + decide _ (BoolLit _) = False + decide l r = not (undefined l r) + BoolOr x y -> loop x && loop y && decide x y + where + decide (BoolLit _) _ = False + decide _ (BoolLit _) = False + decide l r = not (undefined l r) + BoolEQ x y -> loop x && loop y && decide x y + where + decide (BoolLit True) _ = False + decide _ (BoolLit True) = False + decide l r = not (undefined l r) + BoolNE x y -> loop x && loop y && decide x y + where + decide (BoolLit False) _ = False + decide _ (BoolLit False ) = False + decide l r = not (undefined l r) + BoolIf x y z -> + loop x && loop y && loop z && decide x y z + where + decide (BoolLit _) _ _ = False + decide _ (BoolLit True) (BoolLit False) = False + decide _ l r = not (undefined l r) + Natural -> True + NaturalLit _ -> True + NaturalFold -> True + NaturalBuild -> True + NaturalIsZero -> True + NaturalEven -> True + NaturalOdd -> True + NaturalShow -> True + NaturalSubtract -> True + NaturalToInteger -> True + NaturalPlus x y -> loop x && loop y && decide x y + where + decide (NaturalLit 0) _ = False + decide _ (NaturalLit 0) = False + decide (NaturalLit _) (NaturalLit _) = False + decide _ _ = True + NaturalTimes x y -> loop x && loop y && decide x y + where + decide (NaturalLit 0) _ = False + decide _ (NaturalLit 0) = False + decide (NaturalLit 1) _ = False + decide _ (NaturalLit 1) = False + decide (NaturalLit _) (NaturalLit _) = False + decide _ _ = True + Integer -> True + IntegerLit _ -> True + IntegerShow -> True + IntegerToDouble -> True + Double -> True + DoubleLit _ -> True + DoubleShow -> True + String -> True + StringLit _ -> False + StringAppend _ _ -> False + StringShow -> True + List -> True + ListLit t es -> all loop t && all loop es + ListAppend x y -> loop x && loop y && decide x y + where + decide (ListLit _ m) _ | null m = False + decide _ (ListLit _ n) | null n = False + decide (ListLit _ _) (ListLit _ _) = False + decide _ _ = True + ListBuild -> True + ListFold -> True + ListLength -> True + ListHead -> True + ListLast -> True + ListIndexed -> True + ListReverse -> True + Optional -> True + Some a -> loop a + None -> True + OptionalFold -> True + OptionalBuild -> True + Record kts -> undefined kts && all loop (map snd kts) + RecordLit kvs -> undefined kvs && all loop (map snd kvs) + Union kts -> undefined kts && all (all loop) (map snd kts) + Combine x y -> loop x && loop y && decide x y + where + decide (RecordLit m) _ | null m = False + decide _ (RecordLit n) | null n = False + decide (RecordLit _) (RecordLit _) = False + decide _ _ = True + CombineTypes x y -> loop x && loop y && decide x y + where + decide (Record m) _ | null m = False + decide _ (Record n) | null n = False + decide (Record _) (Record _) = False + decide _ _ = True + Prefer x y -> loop x && loop y && decide x y + where + decide (RecordLit m) _ | null m = False + decide _ (RecordLit n) | null n = False + decide (RecordLit _) (RecordLit _) = False + decide l r = not (undefined l r) + Merge x y t -> loop x && loop y && all loop t + ToMap x t -> case x of + RecordLit _ -> False + _ -> loop x && all loop t + Field r k -> case r of + RecordLit _ -> False + Project _ _ -> False + Prefer (RecordLit m) _ -> map fst m == [k] && loop r + Prefer _ (RecordLit _) -> False + Combine (RecordLit m) _ -> map fst m == [k] && loop r + Combine _ (RecordLit m) -> map fst m == [k] && loop r + _ -> loop r + Project r p -> loop r && + case p of + Left s -> case r of + RecordLit _ -> False + _ -> not (null s) && undefined s + Right e' -> case e' of + Record _ -> False + _ -> loop e' + Assert t -> loop t + Equivalent l r -> loop l && loop r + Note _ e' -> loop e' + ImportAlt l _r -> loop l + Embed _ -> True + +{-# COMPLETE + Let' + , Const + , Var + , Lam + , Pi + , App + , Annot + , Bool + , BoolLit + , BoolAnd + , BoolOr + , BoolEQ + , BoolNE + , BoolIf + , Natural + , NaturalLit + , NaturalFold + , NaturalBuild + , NaturalIsZero + , NaturalEven + , NaturalOdd + , NaturalToInteger + , NaturalShow + , NaturalSubtract + , NaturalPlus + , NaturalTimes + , Integer + , IntegerLit + , IntegerShow + , IntegerToDouble + , Double + , DoubleLit + , DoubleShow + , String + , StringLit + , StringAppend + , StringShow + , List + , ListLit + , ListAppend + , ListBuild + , ListFold + , ListLength + , ListHead + , ListLast + , ListIndexed + , ListReverse + , Optional + , Some + , None + , OptionalFold + , OptionalBuild + , Record + , RecordLit + , Union + , Combine + , CombineTypes + , Prefer + , Merge + , ToMap + , Field + , Project + , Assert + , Equivalent + , Note + , ImportAlt + , Embed + #-} +pattern Let' x mA a b = Let x mA a b diff --git a/testsuite/tests/pmcheck/should_compile/T2204.stderr b/testsuite/tests/pmcheck/should_compile/T2204.stderr index c2e2251fc9..26f70352e2 100644 --- a/testsuite/tests/pmcheck/should_compile/T2204.stderr +++ b/testsuite/tests/pmcheck/should_compile/T2204.stderr @@ -3,10 +3,10 @@ T2204.hs:6:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘f’: Patterns not matched: - [] - (p:_) where p is not one of {'0'} - ['0'] + ('0':'1':_:_) ('0':p:_) where p is not one of {'1'} + ['0'] + (p:_) where p is not one of {'0'} ... T2204.hs:9:1: warning: [-Wincomplete-patterns (in -Wextra)] diff --git a/testsuite/tests/pmcheck/should_compile/T9951b.stderr b/testsuite/tests/pmcheck/should_compile/T9951b.stderr index ff6696b466..e4e337b153 100644 --- a/testsuite/tests/pmcheck/should_compile/T9951b.stderr +++ b/testsuite/tests/pmcheck/should_compile/T9951b.stderr @@ -3,8 +3,8 @@ T9951b.hs:7:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘f’: Patterns not matched: - [] - (p:_) where p is not one of {'a'} - ['a'] + ('a':'b':_:_) ('a':p:_) where p is not one of {'b'} + ['a'] + (p:_) where p is not one of {'a'} ... diff --git a/testsuite/tests/pmcheck/should_compile/all.T b/testsuite/tests/pmcheck/should_compile/all.T index 87874f81c8..5a24832945 100644 --- a/testsuite/tests/pmcheck/should_compile/all.T +++ b/testsuite/tests/pmcheck/should_compile/all.T @@ -68,14 +68,20 @@ test('T15584', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('T15713', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) -test('T15753a', expect_broken(15753), compile, +test('T15753a', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('T15753b', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('T15753c', normal, compile, + ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('T15753d', normal, compile, + ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('T15884', expect_broken(15884), compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('T16289', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('T17096', collect_compiler_stats('bytes allocated',10), compile, + ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns +RTS -M2G -RTS']) test('T17112', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) @@ -94,8 +100,14 @@ test('pmc006', [], compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('pmc007', [], compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('pmc008', [], compile, + ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('pmc009', [], compile, + ['-package ghc -fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('T11245', [], compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('T11336b', [], compile, + ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('T12949', [], compile, ['-fwarn-overlapping-patterns']) test('T12957', [], compile, ['-fwarn-overlapping-patterns']) test('T12957a', [], compile, ['-fwarn-overlapping-patterns']) @@ -103,6 +115,8 @@ test('PmExprVars', [], compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) test('CyclicSubst', [], compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) +test('CaseOfKnownCon', [], compile, + ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns']) # EmptyCase test('T10746', [], compile, diff --git a/testsuite/tests/pmcheck/should_compile/pmc007.stderr b/testsuite/tests/pmcheck/should_compile/pmc007.stderr index f6e4ece88c..d4bbe8fb73 100644 --- a/testsuite/tests/pmcheck/should_compile/pmc007.stderr +++ b/testsuite/tests/pmcheck/should_compile/pmc007.stderr @@ -2,24 +2,24 @@ pmc007.hs:7:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘f’: - Patterns not matched: p where p is not one of {"ac", "ab"} + Patterns not matched: p where p is not one of {"ab", "ac"} pmc007.hs:12:1: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In an equation for ‘g’: Patterns not matched: - [] - (p:_) where p is not one of {'a'} + ('a':'b':_:_) + ('a':'c':_:_) + ('a':p:_) where p is not one of {'b', 'c'} ['a'] - ('a':p:_) where p is not one of {'c', 'b'} ... pmc007.hs:18:11: warning: [-Wincomplete-patterns (in -Wextra)] Pattern match(es) are non-exhaustive In a case alternative: Patterns not matched: - [] - (p:_) where p is not one of {'a'} + ('a':'b':_:_) + ('a':'c':_:_) + ('a':p:_) where p is not one of {'b', 'c'} ['a'] - ('a':p:_) where p is not one of {'c', 'b'} ... diff --git a/testsuite/tests/pmcheck/should_compile/pmc008.hs b/testsuite/tests/pmcheck/should_compile/pmc008.hs new file mode 100644 index 0000000000..29e39b573b --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/pmc008.hs @@ -0,0 +1,9 @@ +{-# OPTIONS_GHC -fwarn-incomplete-patterns -fwarn-overlapping-patterns #-} + +module PMC008 where + +-- complete match, but because of the guard, the information that `x` is not +-- `Just` has to flow through the term oracle. +foo :: Maybe Int -> Int +foo x | Just y <- x = y +foo Nothing = 43 diff --git a/testsuite/tests/pmcheck/should_compile/pmc009.hs b/testsuite/tests/pmcheck/should_compile/pmc009.hs new file mode 100644 index 0000000000..ac8f5c2dd5 --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/pmc009.hs @@ -0,0 +1,12 @@ +module HsUtils where +import HsBinds +import SrcLoc + + +-- | We have to be careful to normalise @SrcSpanLess (LHsBind)@ to +-- @LHsBindLR l r@ before passing the representative of @unLoc bind@ on to +-- @mkOneConFull@, otherwise this triggers a panic in @zipTvSubst@. +addPatSynSelector:: LHsBind p -> [a] +addPatSynSelector bind + | PatSynBind _ _ <- unLoc bind + = [] diff --git a/testsuite/tests/pmcheck/should_compile/pmc009.stderr b/testsuite/tests/pmcheck/should_compile/pmc009.stderr new file mode 100644 index 0000000000..8eaa4ab61a --- /dev/null +++ b/testsuite/tests/pmcheck/should_compile/pmc009.stderr @@ -0,0 +1,4 @@ + +pmc009.hs:10:1: warning: [-Wincomplete-patterns (in -Wextra)] + Pattern match(es) are non-exhaustive + In an equation for ‘addPatSynSelector’: Patterns not matched: _ diff --git a/testsuite/tests/th/TH_repUnboxedTuples.stderr b/testsuite/tests/th/TH_repUnboxedTuples.stderr index 5b1f2b386e..8439b12547 100644 --- a/testsuite/tests/th/TH_repUnboxedTuples.stderr +++ b/testsuite/tests/th/TH_repUnboxedTuples.stderr @@ -7,3 +7,7 @@ case (# 'b', GHC.Types.False #) of TH_repUnboxedTuples.hs:18:13: warning: [-Woverlapping-patterns (in -Wdefault)] Pattern match is redundant In a case alternative: (# 'a', True #) -> ... + +TH_repUnboxedTuples.hs:18:13: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In a case alternative: (# _, _ #) -> ... diff --git a/testsuite/tests/typecheck/should_compile/Vta2.stderr b/testsuite/tests/typecheck/should_compile/Vta2.stderr new file mode 100644 index 0000000000..0598cc0226 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/Vta2.stderr @@ -0,0 +1,4 @@ + +Vta2.hs:14:17: warning: [-Woverlapping-patterns (in -Wdefault)] + Pattern match is redundant + In a case alternative: False -> ... |