diff options
Diffstat (limited to 'compiler/deSugar/PmOracle.hs')
-rw-r--r-- | compiler/deSugar/PmOracle.hs | 1872 |
1 files changed, 1872 insertions, 0 deletions
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 |