module Check (
checkSingle, checkMatches, isAnyPmCheckEnabled,
genCaseTmCs1, genCaseTmCs2
) where
#include "HsVersions.h"
import TmOracle
import DynFlags
import HsSyn
import TcHsSyn
import Id
import ConLike
import Name
import FamInstEnv
import TysWiredIn
import TyCon
import SrcLoc
import Util
import Outputable
import FastString
import DataCon
import HscTypes (CompleteMatch(..))
import DsMonad
import TcSimplify (tcCheckSatisfiability)
import TcType (toTcType, isStringTy, isIntTy, isWordTy)
import Bag
import ErrUtils
import Var (EvVar)
import Type
import UniqSupply
import DsGRHSs (isTrueLHsExpr)
import Data.List (find)
import Data.Maybe (isJust, fromMaybe)
import Control.Monad (forM, when, forM_)
import Coercion
import TcEvidence
import IOEnv
import ListT (ListT(..), fold, select)
type PmM a = ListT DsM a
liftD :: DsM a -> PmM a
liftD m = ListT $ \sk fk -> m >>= \a -> sk a fk
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 $ go pmr mpm
go :: Maybe PmResult -> PmResult -> Maybe PmResult
go Nothing rs = Just rs
go old@(Just (PmResult prov rs (UncoveredPatterns us) is)) new
| null us && null rs && null is = old
| otherwise =
let PmResult prov' rs' (UncoveredPatterns us') is' = new
lr = length rs
lr' = length rs'
li = length is
li' = length is'
in case compare (length us) (length us')
`mappend` (compare li li')
`mappend` (compare lr lr')
`mappend` (compare prov prov') of
GT -> Just new
EQ -> Just new
LT -> old
go (Just (PmResult _ _ (TypeOfUncovered _) _)) _new
= panic "getResult: No inhabitation candidates"
data PatTy = PAT | VA
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
PmVar :: { pm_var_id :: Id } -> PmPat t
PmLit :: { pm_lit_lit :: PmLit } -> PmPat t
PmNLit :: { pm_lit_id :: Id
, pm_lit_not :: [PmLit] } -> PmPat 'VA
PmGrd :: { pm_grd_pv :: PatVec
, pm_grd_expr :: PmExpr } -> PmPat 'PAT
type Pattern = PmPat 'PAT
type ValAbs = PmPat 'VA
type PatVec = [Pattern]
data ValVec = ValVec [ValAbs] Delta
data Delta = MkDelta { delta_ty_cs :: Bag EvVar
, delta_tm_cs :: TmState }
type ValSetAbs = [ValVec]
type Uncovered = ValSetAbs
data Covered = Covered | NotCovered
deriving Show
instance Outputable Covered where
ppr (Covered) = text "Covered"
ppr (NotCovered) = text "NotCovered"
instance Monoid Covered where
mempty = NotCovered
Covered `mappend` _ = Covered
_ `mappend` Covered = Covered
NotCovered `mappend` NotCovered = NotCovered
data Diverged = Diverged | NotDiverged
deriving Show
instance Outputable Diverged where
ppr Diverged = text "Diverged"
ppr NotDiverged = text "NotDiverged"
instance Monoid Diverged where
mempty = NotDiverged
Diverged `mappend` _ = Diverged
_ `mappend` Diverged = Diverged
NotDiverged `mappend` NotDiverged = NotDiverged
data Provenance =
FromBuiltin
| FromComplete
deriving (Show, Eq, Ord)
instance Outputable Provenance where
ppr = text . show
instance Monoid Provenance where
mempty = FromBuiltin
FromComplete `mappend` _ = FromComplete
_ `mappend` FromComplete = FromComplete
_ `mappend` _ = FromBuiltin
data PartialResult = PartialResult {
presultProvenence :: Provenance
, 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
instance Monoid PartialResult where
mempty = PartialResult mempty mempty [] mempty
(PartialResult prov1 cs1 vsa1 ds1)
`mappend` (PartialResult prov2 cs2 vsa2 ds2)
= PartialResult (prov1 `mappend` prov2)
(cs1 `mappend` cs2)
(vsa1 `mappend` vsa2)
(ds1 `mappend` ds2)
data PmResult =
PmResult {
pmresultProvenance :: Provenance
, pmresultRedundant :: [Located [LPat Id]]
, pmresultUncovered :: UncoveredCandidates
, pmresultInaccessible :: [Located [LPat Id]] }
data UncoveredCandidates = UncoveredPatterns Uncovered
| TypeOfUncovered Type
emptyPmResult :: PmResult
emptyPmResult = PmResult FromBuiltin [] (UncoveredPatterns []) []
uncoveredWithTy :: Type -> PmResult
uncoveredWithTy ty = PmResult FromBuiltin [] (TypeOfUncovered ty) []
checkSingle :: DynFlags -> DsMatchContext -> Id -> Pat Id -> 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))
case mb_pm_res of
Left _ -> warnPmIters dflags ctxt
Right res -> dsPmWarn dflags ctxt res
checkSingle' :: SrcSpan -> Id -> Pat Id -> PmM PmResult
checkSingle' locn var p = do
liftD resetPmIterDs
fam_insts <- liftD dsGetFamInstEnvs
clause <- liftD $ translatePat fam_insts p
missing <- mkInitialUncovered [var]
tracePm "checkSingle: missing" (vcat (map pprValVecDebug missing))
PartialResult prov cs us ds <- runMany (pmcheckI clause []) missing
let us' = UncoveredPatterns us
return $ case (cs,ds) of
(Covered, _ ) -> PmResult prov [] us' []
(NotCovered, NotDiverged) -> PmResult prov m us' []
(NotCovered, Diverged ) -> PmResult prov [] us' m
where m = [L locn [L locn p]]
checkMatches :: DynFlags -> DsMatchContext
-> [Id] -> [LMatch Id (LHsExpr Id)] -> DsM ()
checkMatches dflags ctxt vars matches = do
tracePmD "checkMatches" (hang (vcat [ppr ctxt
, ppr vars
, text "Matches:"])
2
(vcat (map ppr matches)))
mb_pm_res <- tryM $ getResult $ case matches of
[] | [var] <- vars -> checkEmptyCase' var
_normal_match -> checkMatches' vars matches
case mb_pm_res of
Left _ -> warnPmIters dflags ctxt
Right res -> dsPmWarn dflags ctxt res
checkMatches' :: [Id] -> [LMatch Id (LHsExpr Id)] -> PmM PmResult
checkMatches' vars matches
| null matches = panic "checkMatches': EmptyCase"
| otherwise = do
liftD resetPmIterDs
missing <- mkInitialUncovered vars
tracePm "checkMatches: missing" (vcat (map pprValVecDebug missing))
(prov, rs,us,ds) <- go matches missing
return $ PmResult {
pmresultProvenance = prov
, pmresultRedundant = map hsLMatchToLPats rs
, pmresultUncovered = UncoveredPatterns us
, pmresultInaccessible = map hsLMatchToLPats ds }
where
go :: [LMatch Id (LHsExpr Id)] -> Uncovered
-> PmM (Provenance
, [LMatch Id (LHsExpr Id)]
, Uncovered
, [LMatch Id (LHsExpr Id)])
go [] missing = return (mempty, [], missing, [])
go (m:ms) missing = do
tracePm "checMatches': 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 "checMatches': go: res" (ppr r)
(ms_prov, rs, final_u, is) <- go ms missing'
let final_prov = prov `mappend` ms_prov
return $ case (cs, ds) of
(Covered, _ ) -> (final_prov, rs, final_u, is)
(NotCovered, NotDiverged) -> (final_prov, m:rs, final_u,is)
(NotCovered, Diverged ) -> (final_prov, rs, final_u, m:is)
hsLMatchToLPats :: LMatch id body -> Located [LPat id]
hsLMatchToLPats (L l (Match _ pats _ _)) = L l pats
checkEmptyCase' :: Id -> PmM PmResult
checkEmptyCase' var = do
tm_css <- map toComplex . bagToList <$> liftD getTmCsDs
case tmOracle initialTmState tm_css of
Just tm_state -> do
ty_css <- liftD getDictsDs
fam_insts <- liftD dsGetFamInstEnvs
mb_candidates <- inhabitationCandidates fam_insts (idType var)
case mb_candidates of
Left ty -> return (uncoveredWithTy ty)
Right candidates -> do
missing_m <- flip concatMapM candidates $ \(va,tm_ct,ty_cs) -> do
let all_ty_cs = unionBags ty_cs ty_css
sat_ty <- tyOracle all_ty_cs
return $ case (sat_ty, tmOracle tm_state (tm_ct:tm_css)) of
(True, Just tm_state') -> [(va, all_ty_cs, tm_state')]
_non_sat -> []
let mkValVec (va,all_ty_cs,tm_state')
= ValVec [va] (MkDelta all_ty_cs tm_state')
uncovered = UncoveredPatterns (map mkValVec missing_m)
return $ if null missing_m
then emptyPmResult
else PmResult FromBuiltin [] uncovered []
Nothing -> return emptyPmResult
inhabitationCandidates :: FamInstEnvs -> Type
-> PmM (Either Type [(ValAbs, ComplexEq, Bag EvVar)])
inhabitationCandidates fam_insts ty
= case pmTopNormaliseType_maybe fam_insts ty of
Just (src_ty, dcs, core_ty) -> alts_to_check src_ty core_ty dcs
Nothing -> alts_to_check ty ty []
where
trivially_inhabited = [ charTyCon, doubleTyCon, floatTyCon
, intTyCon, wordTyCon, word8TyCon ]
build_tm :: ValAbs -> [DataCon] -> ValAbs
build_tm = foldr (\dc e -> PmCon (RealDataCon dc) [] [] [] [e])
alts_to_check :: Type -> Type -> [DataCon]
-> PmM (Either Type [(ValAbs, ComplexEq, Bag EvVar)])
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 var <- liftD $ mkPmId (toTcType core_ty)
let va = build_tm (PmVar var) dcs
return $ Right [(va, mkIdEq var, emptyBag)]
| isClosedAlgType core_ty -> liftD $ do
var <- mkPmId (toTcType core_ty)
alts <- mapM (mkOneConFull var . RealDataCon) (tyConDataCons tc)
return $ Right [(build_tm va dcs, eq, cs) | (va, eq, cs) <- alts]
_other -> return (Left src_ty)
nullaryConPattern :: ConLike -> Pattern
nullaryConPattern con =
PmCon { pm_con_con = con, pm_con_arg_tys = []
, pm_con_tvs = [], pm_con_dicts = [], pm_con_args = [] }
truePattern :: Pattern
truePattern = nullaryConPattern (RealDataCon trueDataCon)
fake_pat :: Pattern
fake_pat = PmGrd { pm_grd_pv = [truePattern]
, pm_grd_expr = PmExprOther EWildPat }
isFakeGuard :: [Pattern] -> PmExpr -> Bool
isFakeGuard [PmCon { pm_con_con = RealDataCon c }] (PmExprOther EWildPat)
| c == trueDataCon = True
| otherwise = False
isFakeGuard _pats _e = False
mkCanFailPmPat :: Type -> DsM PatVec
mkCanFailPmPat ty = do
var <- mkPmVar ty
return [var, fake_pat]
vanillaConPattern :: ConLike -> [Type] -> PatVec -> Pattern
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 }
nilPattern :: Type -> Pattern
nilPattern ty =
PmCon { pm_con_con = RealDataCon nilDataCon, pm_con_arg_tys = [ty]
, pm_con_tvs = [], pm_con_dicts = []
, pm_con_args = [] }
mkListPatVec :: Type -> PatVec -> PatVec -> PatVec
mkListPatVec ty xs ys = [PmCon { pm_con_con = RealDataCon consDataCon
, pm_con_arg_tys = [ty]
, pm_con_tvs = [], pm_con_dicts = []
, pm_con_args = xs++ys }]
mkLitPattern :: HsLit -> Pattern
mkLitPattern lit = PmLit { pm_lit_lit = PmSLit lit }
translatePat :: FamInstEnvs -> Pat Id -> DsM PatVec
translatePat fam_insts pat = case pat of
WildPat ty -> mkPmVars [ty]
VarPat id -> return [PmVar (unLoc id)]
ParPat p -> translatePat fam_insts (unLoc p)
LazyPat _ -> mkPmVars [hsPatType pat]
BangPat p -> translatePat fam_insts (unLoc p)
AsPat lid p -> do
ps <- translatePat fam_insts (unLoc p)
let [e] = map vaToPmExpr (coercePatVec ps)
g = PmGrd [PmVar (unLoc lid)] e
return (ps ++ [g])
SigPatOut p _ty -> translatePat fam_insts (unLoc p)
CoPat wrapper p ty
| isIdHsWrapper wrapper -> translatePat fam_insts p
| WpCast co <- wrapper, isReflexiveCo co -> translatePat fam_insts p
| otherwise -> do
ps <- translatePat fam_insts p
(xp,xe) <- mkPmId2Forms ty
let g = mkGuard ps (HsWrap wrapper (unLoc xe))
return [xp,g]
NPlusKPat (L _ _n) _k1 _k2 _ge _minus ty -> mkCanFailPmPat ty
ViewPat lexpr lpat arg_ty -> do
ps <- translatePat fam_insts (unLoc lpat)
case all cantFailPattern ps of
True -> do
(xp,xe) <- mkPmId2Forms arg_ty
let g = mkGuard ps (HsApp lexpr xe)
return [xp,g]
False -> mkCanFailPmPat arg_ty
ListPat ps ty Nothing -> do
foldr (mkListPatVec ty) [nilPattern ty]
<$> translatePatVec fam_insts (map unLoc ps)
ListPat lpats elem_ty (Just (pat_ty, _to_list))
| Just e_ty <- splitListTyConApp_maybe pat_ty
, (_, norm_elem_ty) <- normaliseType fam_insts Nominal elem_ty
, norm_elem_ty `eqType` e_ty ->
translatePat fam_insts (ListPat lpats e_ty Nothing)
| otherwise -> mkCanFailPmPat pat_ty
ConPatOut { pat_con = L _ con
, pat_arg_tys = arg_tys
, pat_tvs = ex_tvs
, pat_dicts = dicts
, pat_args = ps } -> do
groups <- allCompleteMatches con arg_tys
case groups of
[] -> mkCanFailPmPat (conLikeResTy con arg_tys)
_ -> do
args <- translateConPatVec fam_insts arg_tys ex_tvs con ps
return [PmCon { pm_con_con = con
, pm_con_arg_tys = arg_tys
, pm_con_tvs = ex_tvs
, pm_con_dicts = dicts
, pm_con_args = args }]
NPat (L _ ol) mb_neg _eq ty -> translateNPat fam_insts ol mb_neg ty
LitPat lit
| HsString src s <- lit ->
foldr (mkListPatVec charTy) [nilPattern charTy] <$>
translatePatVec fam_insts (map (LitPat . HsChar src) (unpackFS s))
| otherwise -> return [mkLitPattern lit]
PArrPat ps ty -> do
tidy_ps <- translatePatVec fam_insts (map unLoc ps)
let fake_con = RealDataCon (parrFakeCon (length ps))
return [vanillaConPattern fake_con [ty] (concat tidy_ps)]
TuplePat ps boxity tys -> do
tidy_ps <- translatePatVec fam_insts (map unLoc ps)
let tuple_con = RealDataCon (tupleDataCon boxity (length ps))
return [vanillaConPattern tuple_con tys (concat tidy_ps)]
SumPat p alt arity ty -> do
tidy_p <- translatePat fam_insts (unLoc p)
let sum_con = RealDataCon (sumDataCon alt arity)
return [vanillaConPattern sum_con ty tidy_p]
ConPatIn {} -> panic "Check.translatePat: ConPatIn"
SplicePat {} -> panic "Check.translatePat: SplicePat"
SigPatIn {} -> panic "Check.translatePat: SigPatIn"
translateNPat :: FamInstEnvs
-> HsOverLit Id -> Maybe (SyntaxExpr Id) -> Type -> DsM PatVec
translateNPat fam_insts (OverLit val False _ ty) mb_neg outer_ty
| not type_change, isStringTy ty, HsIsString src s <- val, Nothing <- mb_neg
= translatePat fam_insts (LitPat (HsString src s))
| not type_change, isIntTy ty, HsIntegral src i <- val
= translatePat fam_insts (mk_num_lit HsInt src i)
| not type_change, isWordTy ty, HsIntegral src i <- val
= translatePat fam_insts (mk_num_lit HsWordPrim src i)
where
type_change = not (outer_ty `eqType` ty)
mk_num_lit c src i = LitPat $ case mb_neg of
Nothing -> c src i
Just _ -> c src (i)
translateNPat _ ol mb_neg _
= return [PmLit { pm_lit_lit = PmOLit (isJust mb_neg) ol }]
translatePatVec :: FamInstEnvs -> [Pat Id] -> DsM [PatVec]
translatePatVec fam_insts pats = mapM (translatePat fam_insts) pats
translateConPatVec :: FamInstEnvs -> [Type] -> [TyVar]
-> ConLike -> HsConPatDetails Id -> 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)
= concat <$> translatePatVec fam_insts (map unLoc [p1,p2])
translateConPatVec fam_insts univ_tys ex_tvs c (RecCon (HsRecFields fs _))
| null fs = mkPmVars arg_tys
| null orig_lbls = ASSERT(null matched_lbls) mkPmVars arg_tys
| matched_lbls `subsetOf` orig_lbls
= ASSERT(length orig_lbls == length arg_tys)
let translateOne (lbl, ty) = case lookup lbl matched_pats of
Just p -> translatePat fam_insts p
Nothing -> mkPmVars [ty]
in concatMapM translateOne (zip orig_lbls arg_tys)
| otherwise = do
arg_var_pats <- mkPmVars arg_tys
translated_pats <- forM matched_pats $ \(x,pat) -> do
pvec <- translatePat fam_insts pat
return (x, pvec)
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))
Nothing -> panic "translateConPatVec: lookup")
translated_pats
return (arg_var_pats ++ guards)
where
arg_tys = conLikeInstOrigArgTys c (univ_tys ++ mkTyVarTys ex_tvs)
orig_lbls = map flSelector $ conLikeFieldLabels c
matched_pats = [ (getName (unLoc (hsRecFieldId x)), unLoc (hsRecFieldArg x))
| L _ x <- fs]
matched_lbls = [ name | (name, _pat) <- matched_pats ]
subsetOf :: Eq a => [a] -> [a] -> Bool
subsetOf [] _ = True
subsetOf (_:_) [] = False
subsetOf (x:xs) (y:ys)
| x == y = subsetOf xs ys
| otherwise = subsetOf (x:xs) ys
translateMatch :: FamInstEnvs -> LMatch Id (LHsExpr Id) -> DsM (PatVec,[PatVec])
translateMatch fam_insts (L _ (Match _ lpats _ grhss)) = do
pats' <- concat <$> translatePatVec fam_insts pats
guards' <- mapM (translateGuards fam_insts) guards
return (pats', guards')
where
extractGuards :: LGRHS Id (LHsExpr Id) -> [GuardStmt Id]
extractGuards (L _ (GRHS gs _)) = map unLoc gs
pats = map unLoc lpats
guards = map extractGuards (grhssGRHSs grhss)
translateGuards :: FamInstEnvs -> [GuardStmt Id] -> DsM PatVec
translateGuards fam_insts guards = do
all_guards <- concat <$> mapM (translateGuard fam_insts) guards
return (replace_unhandled all_guards)
where
replace_unhandled :: PatVec -> PatVec
replace_unhandled gv
| any_unhandled gv = fake_pat : [ p | p <- gv, shouldKeep p ]
| otherwise = gv
any_unhandled :: PatVec -> Bool
any_unhandled gv = any (not . shouldKeep) gv
shouldKeep :: Pattern -> Bool
shouldKeep p
| PmVar {} <- p = True
| PmCon {} <- p = singleConstructor (pm_con_con p)
&& all shouldKeep (pm_con_args p)
shouldKeep (PmGrd pv e)
| all shouldKeep pv = True
| isNotPmExprOther e = True
shouldKeep _other_pat = False
cantFailPattern :: Pattern -> Bool
cantFailPattern p
| PmVar {} <- p = True
| PmCon {} <- p = singleConstructor (pm_con_con p)
&& all cantFailPattern (pm_con_args p)
cantFailPattern (PmGrd pv _e)
= all cantFailPattern pv
cantFailPattern _ = False
translateGuard :: FamInstEnvs -> GuardStmt Id -> DsM PatVec
translateGuard fam_insts guard = case guard of
BodyStmt e _ _ _ -> translateBoolGuard e
LetStmt binds -> translateLet (unLoc binds)
BindStmt p e _ _ _ -> translateBind fam_insts p e
LastStmt {} -> panic "translateGuard LastStmt"
ParStmt {} -> panic "translateGuard ParStmt"
TransStmt {} -> panic "translateGuard TransStmt"
RecStmt {} -> panic "translateGuard RecStmt"
ApplicativeStmt {} -> panic "translateGuard ApplicativeLastStmt"
translateLet :: HsLocalBinds Id -> DsM PatVec
translateLet _binds = return []
translateBind :: FamInstEnvs -> LPat Id -> LHsExpr Id -> DsM PatVec
translateBind fam_insts (L _ p) e = do
ps <- translatePat fam_insts p
return [mkGuard ps (unLoc e)]
translateBoolGuard :: LHsExpr Id -> DsM PatVec
translateBoolGuard e
| isJust (isTrueLHsExpr e) = return []
| otherwise = return [mkGuard [truePattern] (unLoc e)]
pmPatType :: PmPat p -> Type
pmPatType (PmCon { pm_con_con = con, pm_con_arg_tys = tys })
= conLikeResTy 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
mkOneConFull :: Id -> ConLike -> DsM (ValAbs, ComplexEq, Bag EvVar)
mkOneConFull x con = do
let
res_ty = idType x
(univ_tvs, ex_tvs, eq_spec, thetas, _req_theta , arg_tys, _)
= conLikeFullSig con
tc_args = case splitTyConApp_maybe res_ty of
Just (_, tys) -> tys
Nothing -> pprPanic "mkOneConFull: Not TyConApp:" (ppr res_ty)
subst1 = zipTvSubst univ_tvs tc_args
(subst, ex_tvs') <- cloneTyVarBndrs subst1 ex_tvs <$> getUniqueSupplyM
arguments <- mapM mkPmVar (substTys subst arg_tys)
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 }
return (con_abs, (PmExprVar (idName x), vaToPmExpr con_abs), listToBag evvars)
mkGuard :: PatVec -> HsExpr Id -> Pattern
mkGuard pv e
| all cantFailPattern pv = PmGrd pv expr
| PmExprOther {} <- expr = fake_pat
| otherwise = PmGrd pv expr
where
expr = hsExprToPmExpr e
mkNegEq :: Id -> PmLit -> ComplexEq
mkNegEq x l = (falsePmExpr, PmExprVar (idName x) `PmExprEq` PmExprLit l)
mkPosEq :: Id -> PmLit -> ComplexEq
mkPosEq x l = (PmExprVar (idName x), PmExprLit l)
mkIdEq :: Id -> ComplexEq
mkIdEq x = (PmExprVar name, PmExprVar name)
where name = idName x
mkPmVar :: Type -> DsM (PmPat p)
mkPmVar ty = PmVar <$> mkPmId ty
mkPmVars :: [Type] -> DsM PatVec
mkPmVars tys = mapM mkPmVar tys
mkPmId :: Type -> DsM Id
mkPmId ty = getUniqueM >>= \unique ->
let occname = mkVarOccFS (fsLit (show unique))
name = mkInternalName unique occname noSrcSpan
in return (mkLocalId name ty)
mkPmId2Forms :: Type -> DsM (Pattern, LHsExpr Id)
mkPmId2Forms ty = do
x <- mkPmId ty
return (PmVar x, noLoc (HsVar (noLoc x)))
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)
coercePatVec :: PatVec -> [ValAbs]
coercePatVec pv = concatMap coercePmPat pv
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 {}) = []
singleConstructor :: ConLike -> Bool
singleConstructor (RealDataCon dc) =
case tyConDataCons (dataConTyCon dc) of
[_] -> True
_ -> False
singleConstructor _ = False
allCompleteMatches :: ConLike -> [Type] -> DsM [(Provenance, [ConLike])]
allCompleteMatches cl tys = do
let fam = case cl of
RealDataCon dc ->
[(FromBuiltin, map RealDataCon (tyConDataCons (dataConTyCon dc)))]
PatSynCon _ -> []
pragmas <- case splitTyConApp_maybe (conLikeResTy cl tys) of
Just (tc, _) -> dsGetCompleteMatches tc
Nothing -> return []
let fams cm = fmap (FromComplete,) $
mapM dsLookupConLike (completeMatchConLikes cm)
from_pragma <- mapM fams pragmas
let final_groups = fam ++ from_pragma
tracePmD "allCompleteMatches" (ppr final_groups)
return final_groups
newEvVar :: Name -> Type -> EvVar
newEvVar name ty = mkLocalId name (toTcType 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)
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) }
type PmArity = Int
patVecArity :: PatVec -> PmArity
patVecArity = sum . map patternArity
patternArity :: Pattern -> PmArity
patternArity (PmGrd {}) = 0
patternArity _other_pat = 1
runMany :: (ValVec -> PmM PartialResult) -> (Uncovered -> PmM PartialResult)
runMany _ [] = return mempty
runMany pm (m:ms) = mappend <$> pm m <*> runMany pm ms
mkInitialUncovered :: [Id] -> PmM Uncovered
mkInitialUncovered vars = do
ty_cs <- liftD getDictsDs
tm_cs <- map toComplex . 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)
patterns = map PmVar vars
return [ValVec patterns (MkDelta initTyCs initTmState)]
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
tracePm "pmCheckResult:" (ppr res)
return res
pmcheckGuardsI :: [PatVec] -> ValVec -> PmM PartialResult
pmcheckGuardsI gvs vva = liftD incrCheckPmIterDs >> pmcheckGuards gvs vva
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
pmcheck :: PatVec -> [PatVec] -> ValVec -> PmM PartialResult
pmcheck [] guards vva@(ValVec [] _)
| null guards = return $ mempty { presultCovered = Covered }
| otherwise = pmcheckGuardsI guards vva
pmcheck (p@(PmGrd pv e) : ps) guards vva@(ValVec vas delta)
| isFakeGuard pv e = forces . mkCons vva <$> pmcheckI ps guards vva
| otherwise = 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)
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)
pmcheckHd :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec
-> PmM PartialResult
pmcheckHd (PmVar x) ps guards va (ValVec vva delta)
| Just tm_state <- solveOneEq (delta_tm_cs delta)
(PmExprVar (idName x), vaToPmExpr va)
= ucon va <$> pmcheckI ps guards (ValVec vva (delta {delta_tm_cs = tm_state}))
| otherwise = return mempty
pmcheckHd ( p@(PmCon {pm_con_con = c1, pm_con_args = args1})) ps guards
(va@(PmCon {pm_con_con = c2, pm_con_args = args2})) (ValVec vva delta)
| c1 /= c2 =
return (usimple [ValVec (va:vva) delta])
| otherwise = kcon c1 (pm_con_arg_tys p) (pm_con_tvs p) (pm_con_dicts p)
<$> pmcheckI (args1 ++ ps) guards (ValVec (args2 ++ vva) delta)
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])
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) complete_match
inst_vsa <- flip concatMapM cons_cs $ \(va, tm_ct, ty_cs) -> do
let ty_state = ty_cs `unionBags` delta_ty_cs delta
sat_ty <- if isEmptyBag ty_cs then return True
else tyOracle ty_state
return $ case (sat_ty, solveOneEq (delta_tm_cs delta) tm_ct) of
(True, Just tm_state) -> [ValVec (va:vva) (MkDelta ty_state tm_state)]
_ty_or_tm_failed -> []
set_provenance prov .
force_if (canDiverge (idName x) (delta_tm_cs delta)) <$>
runMany (pmcheckI (p:ps) guards) inst_vsa
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
us | Just tm_state <- solveOneEq (delta_tm_cs delta) (mkNegEq x l)
= [ValVec (PmNLit x [l] : vva) (delta { delta_tm_cs = tm_state })]
| otherwise = []
non_matched = usimple us
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)
= mkUnion non_matched <$>
pmcheckHdI p ps guards (PmLit l)
(ValVec vva (delta { delta_tm_cs = tm_state }))
| otherwise = return non_matched
where
us | Just tm_state <- solveOneEq (delta_tm_cs delta) (mkNegEq x l)
= [ValVec (PmNLit x (l:lits) : vva) (delta { delta_tm_cs = tm_state })]
| otherwise = []
non_matched = usimple us
pmcheckHd (PmLit l) ps guards (va@(PmCon {})) (ValVec vva delta)
= do y <- liftD $ mkPmId (pmPatType va)
let tm_state = extendSubst y (PmExprLit l) (delta_tm_cs delta)
delta' = delta { delta_tm_cs = tm_state }
pmcheckHdI (PmVar y) ps guards va (ValVec vva delta')
pmcheckHd (p@(PmCon {})) ps guards (PmLit l) (ValVec vva delta)
= do y <- liftD $ mkPmId (pmPatType p)
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')
pmcheckHd (p@(PmCon {})) ps guards (PmNLit { pm_lit_id = x }) vva
= pmcheckHdI p ps guards (PmVar x) vva
pmcheckHd (PmGrd {}) _ _ _ _ = panic "pmcheckHd: Guard"
updateVsa :: (ValSetAbs -> ValSetAbs) -> (PartialResult -> PartialResult)
updateVsa f p@(PartialResult { presultUncovered = old })
= p { presultUncovered = f old }
usimple :: ValSetAbs -> PartialResult
usimple vsa = mempty { presultUncovered = vsa }
utail :: PartialResult -> PartialResult
utail = updateVsa upd
where upd vsa = [ ValVec vva delta | ValVec (_:vva) delta <- vsa ]
ucon :: ValAbs -> PartialResult -> PartialResult
ucon va = updateVsa upd
where
upd vsa = [ ValVec (va:vva) delta | ValVec vva delta <- vsa ]
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
mkUnion :: PartialResult -> PartialResult -> PartialResult
mkUnion = mappend
mkCons :: ValVec -> PartialResult -> PartialResult
mkCons vva = updateVsa (vva:)
forces :: PartialResult -> PartialResult
forces pres = pres { presultDivergent = Diverged }
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 { presultProvenence = prov }
genCaseTmCs2 :: Maybe (LHsExpr Id)
-> [Pat Id]
-> [Id]
-> DsM (Bag SimpleEq)
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 [(var, e), (var, scr_e)]
genCaseTmCs2 _ _ _ = panic "genCaseTmCs2: HsCase"
genCaseTmCs1 :: Maybe (LHsExpr Id) -> [Id] -> Bag SimpleEq
genCaseTmCs1 Nothing _ = emptyBag
genCaseTmCs1 (Just scr) [var] = unitBag (var, lhsExprToPmExpr scr)
genCaseTmCs1 _ _ = panic "genCaseTmCs1: HsCase"
isAnyPmCheckEnabled :: DynFlags -> DsMatchContext -> Bool
isAnyPmCheckEnabled dflags (DsMatchContext kind _loc)
= wopt Opt_WarnOverlappingPatterns dflags || exhaustive dflags kind
instance Outputable ValVec where
ppr (ValVec vva delta)
= let (residual_eqs, subst) = wrapUpTmState (delta_tm_cs delta)
vector = substInValAbs subst vva
in ppr_uncovered (vector, residual_eqs)
substInValAbs :: PmVarEnv -> [ValAbs] -> [PmExpr]
substInValAbs subst = map (exprDeepLookup subst . vaToPmExpr)
wrapUpTmState :: TmState -> ([ComplexEq], PmVarEnv)
wrapUpTmState (residual, (_, subst)) = (residual, flattenPmVarEnv subst)
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
exists_u = flag_u && (case uncovered of
TypeOfUncovered _ -> True
UncoveredPatterns u -> notNull u)
when exists_r $ forM_ redundant $ \(L l q) -> do
putSrcSpanDs l (warnDs (Reason Opt_WarnOverlappingPatterns)
(pprEqn q "is redundant"))
when exists_i $ forM_ inaccessible $ \(L l q) -> do
putSrcSpanDs l (warnDs (Reason Opt_WarnOverlappingPatterns)
(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
where
PmResult
{ pmresultProvenance = prov
, pmresultRedundant = redundant
, pmresultUncovered = uncovered
, pmresultInaccessible = inaccessible } = pm_result
flag_i = wopt Opt_WarnOverlappingPatterns dflags
flag_u = exhaustive dflags kind
flag_u_reason = maybe NoReason Reason (exhaustiveWarningFlag kind)
is_rec_upd = case kind of { RecUpd -> True; _ -> False }
onlyBuiltin = prov == FromBuiltin
maxPatterns = maxUncoveredPatterns dflags
pprEqn q txt = pp_context True ctx (text txt) $ \f -> ppr_eqn f kind q
pprEqns qs = pp_context False ctx (text "are non-exhaustive") $ \_ ->
case qs of
[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)
warnEmptyCase ty = pp_context False ctx (text "are non-exhaustive") $ \_ ->
hang (text "Patterns not matched:") 4 (underscore <+> dcolon <+> ppr ty)
warnPmIters :: DynFlags -> DsMatchContext -> DsM ()
warnPmIters dflags (DsMatchContext kind loc)
= when (flag_i || flag_u) $ do
iters <- maxPmCheckIterations <$> getDynFlags
putSrcSpanDs loc (warnDs NoReason (msg iters))
where
ctxt = pprMatchContext kind
msg is = fsep [ text "Pattern match checker exceeded"
, parens (ppr is), text "iterations in", ctxt <> dot
, text "(Use -fmax-pmcheck-iterations=n"
, text "to set the maximun number of iterations to n)" ]
flag_i = wopt Opt_WarnOverlappingPatterns dflags
flag_u = exhaustive dflags kind
dots :: Int -> [a] -> SDoc
dots maxPatterns qs
| qs `lengthExceeds` maxPatterns = text "..."
| otherwise = empty
exhaustive :: DynFlags -> HsMatchContext id -> Bool
exhaustive dflags = maybe False (`wopt` dflags) . exhaustiveWarningFlag
exhaustiveWarningFlag :: HsMatchContext id -> Maybe WarningFlag
exhaustiveWarningFlag (FunRhs {}) = Just Opt_WarnIncompletePatterns
exhaustiveWarningFlag CaseAlt = Just Opt_WarnIncompletePatterns
exhaustiveWarningFlag IfAlt = Nothing
exhaustiveWarningFlag LambdaExpr = Just Opt_WarnIncompleteUniPatterns
exhaustiveWarningFlag PatBindRhs = Just Opt_WarnIncompleteUniPatterns
exhaustiveWarningFlag ProcExpr = Just Opt_WarnIncompleteUniPatterns
exhaustiveWarningFlag RecUpd = Just Opt_WarnIncompletePatternsRecUpd
exhaustiveWarningFlag ThPatSplice = Nothing
exhaustiveWarningFlag PatSyn = Nothing
exhaustiveWarningFlag ThPatQuote = Nothing
exhaustiveWarningFlag (StmtCtxt {}) = Nothing
pp_context :: Bool -> DsMatchContext -> SDoc -> ((SDoc -> SDoc) -> SDoc) -> SDoc
pp_context singular (DsMatchContext kind _loc) msg rest_of_msg_fun
= vcat [text txt <+> msg,
sep [ text "In" <+> ppr_match <> char ':'
, nest 4 (rest_of_msg_fun pref)]]
where
txt | singular = "Pattern match"
| otherwise = "Pattern match(es)"
(ppr_match, pref)
= case kind of
FunRhs (L _ fun) _ -> (pprMatchContext kind,
\ pp -> ppr fun <+> pp)
_ -> (pprMatchContext kind, \ pp -> pp)
ppr_pats :: HsMatchContext Name -> [Pat Id] -> SDoc
ppr_pats kind pats
= sep [sep (map ppr pats), matchSeparator kind, text "..."]
ppr_eqn :: (SDoc -> SDoc) -> HsMatchContext Name -> [LPat Id] -> SDoc
ppr_eqn prefixF kind eqn = prefixF (ppr_pats kind (map unLoc eqn))
ppr_constraint :: (SDoc,[PmLit]) -> SDoc
ppr_constraint (var, lits) = var <+> text "is not one of"
<+> braces (pprWithCommas ppr lits)
ppr_uncovered :: ([PmExpr], [ComplexEq]) -> SDoc
ppr_uncovered (expr_vec, complex)
| null cs = fsep vec
| otherwise = hang (fsep vec) 4 $
text "where" <+> vcat (map ppr_constraint cs)
where
sdoc_vec = mapM pprPmExprWithParens expr_vec
(vec,cs) = runPmPprM sdoc_vec (filterComplex complex)
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
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)