summaryrefslogtreecommitdiff
path: root/compiler/GHC/HsToCore/PmCheck.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/HsToCore/PmCheck.hs')
-rw-r--r--compiler/GHC/HsToCore/PmCheck.hs148
1 files changed, 67 insertions, 81 deletions
diff --git a/compiler/GHC/HsToCore/PmCheck.hs b/compiler/GHC/HsToCore/PmCheck.hs
index 2a7d70abd2..9ff4a58ae7 100644
--- a/compiler/GHC/HsToCore/PmCheck.hs
+++ b/compiler/GHC/HsToCore/PmCheck.hs
@@ -17,7 +17,7 @@ module GHC.HsToCore.PmCheck (
needToRunPmCheck, isMatchContextPmChecked,
-- See Note [Type and Term Equality Propagation]
- addTyCsDs, addScrutTmCs, addPatTmCs
+ addTyCsDs, addScrutTmCs
) where
#include "HsVersions.h"
@@ -109,8 +109,8 @@ data PmGrd
-- | @PmLet x expr@ corresponds to a @let x = expr@ guard. This actually
-- /binds/ @x@.
| PmLet {
- pm_id :: !Id,
- pm_let_expr :: !CoreExpr
+ pm_id :: !Id,
+ _pm_let_expr :: !CoreExpr
}
-- | Should not be user-facing.
@@ -160,10 +160,11 @@ data GrdTree
-- tree. 'redundantAndInaccessibleRhss' can figure out redundant and proper
-- inaccessible RHSs from this.
data AnnotatedTree
- = AccessibleRhs !RhsInfo
- -- ^ A RHS deemed accessible.
+ = AccessibleRhs !Deltas !RhsInfo
+ -- ^ A RHS deemed accessible. The 'Deltas' is the (non-empty) set of covered
+ -- values.
| InaccessibleRhs !RhsInfo
- -- ^ A RHS deemed inaccessible; no value could possibly reach it.
+ -- ^ A RHS deemed inaccessible; it covers no value.
| MayDiverge !AnnotatedTree
-- ^ Asserts that the tree may force diverging values, so not all of its
-- clauses can be redundant.
@@ -194,7 +195,7 @@ instance Outputable GrdTree where
ppr Empty = text "<empty case>"
instance Outputable AnnotatedTree where
- ppr (AccessibleRhs info) = pprRhsInfo info
+ ppr (AccessibleRhs _ info) = pprRhsInfo info
ppr (InaccessibleRhs info) = text "inaccessible" <+> pprRhsInfo info
ppr (MayDiverge t) = text "div" <+> ppr t
-- Format nested Sequences in blocks "{ grds1; grds2; ... }"
@@ -204,17 +205,6 @@ instance Outputable AnnotatedTree where
collect_seqs t = [ppr t]
ppr EmptyAnn = text "<empty case>"
-newtype Deltas = MkDeltas (Bag Delta)
-
-instance Outputable Deltas where
- ppr (MkDeltas deltas) = ppr deltas
-
-instance Semigroup Deltas where
- MkDeltas l <> MkDeltas r = MkDeltas (l `unionBags` r)
-
-liftDeltasM :: Monad m => (Delta -> m (Maybe Delta)) -> Deltas -> m Deltas
-liftDeltasM f (MkDeltas ds) = MkDeltas . catBagMaybes <$> (traverse f ds)
-
-- | Lift 'addPmCts' over 'Deltas'.
addPmCtsDeltas :: Deltas -> PmCts -> DsM Deltas
addPmCtsDeltas deltas cts = liftDeltasM (\d -> addPmCts d cts) deltas
@@ -272,7 +262,8 @@ checkSingle dflags ctxt@(DsMatchContext kind locn) var p = do
-- Omitting checking this flag emits redundancy warnings twice in obscure
-- cases like #17646.
when (exhaustive dflags kind) $ do
- missing <- MkDeltas . unitBag <$> getPmDelta
+ -- TODO: This could probably call checkMatches, like checkGuardMatches.
+ missing <- getPmDeltas
tracePm "checkSingle: missing" (ppr missing)
fam_insts <- dsGetFamInstEnvs
grd_tree <- mkGrdTreeRhs (L locn $ ppr p) <$> translatePat fam_insts var p
@@ -280,12 +271,13 @@ checkSingle dflags ctxt@(DsMatchContext kind locn) var p = do
dsPmWarn dflags ctxt [var] res
-- | Exhaustive for guard matches, is used for guards in pattern bindings and
--- in @MultiIf@ expressions.
-checkGuardMatches :: HsMatchContext GhcRn -- Match context
- -> GRHSs GhcTc (LHsExpr GhcTc) -- Guarded RHSs
- -> DsM ()
+-- in @MultiIf@ expressions. Returns the 'Deltas' covered by the RHSs.
+checkGuardMatches
+ :: HsMatchContext GhcRn -- ^ Match context, for warning messages
+ -> GRHSs GhcTc (LHsExpr GhcTc) -- ^ The GRHSs to check
+ -> DsM [Deltas] -- ^ Covered 'Deltas' for each RHS, for long
+ -- distance info
checkGuardMatches hs_ctx guards@(GRHSs _ grhss _) = do
- dflags <- getDynFlags
let combinedLoc = foldl1 combineSrcSpans (map getLoc grhss)
dsMatchContext = DsMatchContext hs_ctx combinedLoc
match = L combinedLoc $
@@ -293,20 +285,37 @@ checkGuardMatches hs_ctx guards@(GRHSs _ grhss _) = do
, m_ctxt = hs_ctx
, m_pats = []
, m_grhss = guards }
- checkMatches dflags dsMatchContext [] [match]
+ checkMatches dsMatchContext [] [match]
checkGuardMatches _ (XGRHSs nec) = noExtCon nec
--- | Check a matchgroup (case, functions, etc.)
-checkMatches :: DynFlags -> DsMatchContext
- -> [Id] -> [LMatch GhcTc (LHsExpr GhcTc)] -> DsM ()
-checkMatches dflags ctxt vars matches = do
+-- | Check a list of syntactic /match/es (part of case, functions, etc.), each
+-- with a /pat/ and one or more /grhss/:
+--
+-- @
+-- f x y | x == y = 1 -- match on x and y with two guarded RHSs
+-- | otherwise = 2
+-- f _ _ = 3 -- clause with a single, un-guarded RHS
+-- @
+--
+-- Returns one 'Deltas' for each GRHS, representing its covered values, or the
+-- incoming uncovered 'Deltas' (from 'getPmDeltas') if the GRHS is inaccessible.
+-- Since there is at least one /grhs/ per /match/, the list of 'Deltas' is at
+-- least as long as the list of matches.
+checkMatches
+ :: DsMatchContext -- ^ Match context, for warnings messages
+ -> [Id] -- ^ Match variables, i.e. x and y above
+ -> [LMatch GhcTc (LHsExpr GhcTc)] -- ^ List of matches
+ -> DsM [Deltas] -- ^ One covered 'Deltas' per RHS, for long
+ -- distance info.
+checkMatches ctxt vars matches = do
+ dflags <- getDynFlags
tracePm "checkMatches" (hang (vcat [ppr ctxt
, ppr vars
, text "Matches:"])
2
(vcat (map ppr matches)))
- init_deltas <- MkDeltas . unitBag <$> getPmDelta
+ init_deltas <- getPmDeltas
missing <- case matches of
-- This must be an -XEmptyCase. See Note [Checking EmptyCase]
[] | [var] <- vars -> addPmCtDeltas init_deltas (PmNotBotCt var)
@@ -317,6 +326,21 @@ checkMatches dflags ctxt vars matches = do
dsPmWarn dflags ctxt vars res
+ return (extractRhsDeltas init_deltas (cr_clauses res))
+
+-- | Extract the 'Deltas' reaching the RHSs of the 'AnnotatedTree'.
+-- For 'AccessibleRhs's, this is stored in the tree node, whereas
+-- 'InaccessibleRhs's fall back to the supplied original 'Deltas'.
+-- See @Note [Recovering from unsatisfiable pattern-matching constraints]@.
+extractRhsDeltas :: Deltas -> AnnotatedTree -> [Deltas]
+extractRhsDeltas orig_deltas = fromOL . go
+ where
+ go (AccessibleRhs deltas _) = unitOL deltas
+ go (InaccessibleRhs _) = unitOL orig_deltas
+ go (MayDiverge t) = go t
+ go (SequenceAnn l r) = go l Semi.<> go r
+ go EmptyAnn = nilOL
+
{- Note [Checking EmptyCase]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-XEmptyCase is useful for matching on empty data types like 'Void'. For example,
@@ -920,7 +944,9 @@ checkGrdTree' :: GrdTree -> Deltas -> DsM CheckResult
-- RHS: Check that it covers something and wrap Inaccessible if not
checkGrdTree' (Rhs sdoc) deltas = do
is_covered <- isInhabited deltas
- let clauses = if is_covered then AccessibleRhs sdoc else InaccessibleRhs sdoc
+ let clauses
+ | is_covered = AccessibleRhs deltas sdoc
+ | otherwise = InaccessibleRhs sdoc
pure CheckResult
{ cr_clauses = clauses
, cr_uncov = MkDeltas emptyBag
@@ -1005,22 +1031,24 @@ f x = case x of
(_:_) -> True
[] -> False -- can't happen
-Functions `addScrutTmCs' and `addPatTmCs' are responsible for generating
+Functions `addScrutTmCs' is responsible for generating
these constraints.
-}
-locallyExtendPmDelta :: (Delta -> DsM (Maybe Delta)) -> DsM a -> DsM a
-locallyExtendPmDelta ext k = getPmDelta >>= ext >>= \case
+locallyExtendPmDelta :: (Deltas -> DsM Deltas) -> DsM a -> DsM a
+locallyExtendPmDelta ext k = getPmDeltas >>= ext >>= \deltas -> do
+ inh <- isInhabited deltas
-- If adding a constraint would lead to a contradiction, don't add it.
-- See @Note [Recovering from unsatisfiable pattern-matching constraints]@
-- for why this is done.
- Nothing -> k
- Just delta' -> updPmDelta delta' k
+ if inh
+ then updPmDeltas deltas k
+ else k
-- | Add in-scope type constraints
addTyCsDs :: Bag EvVar -> DsM a -> DsM a
addTyCsDs ev_vars =
- locallyExtendPmDelta (\delta -> addPmCts delta (PmTyCt . evVarPred <$> ev_vars))
+ locallyExtendPmDelta (\deltas -> addPmCtsDeltas deltas (PmTyCt . evVarPred <$> ev_vars))
-- | Add equalities for the scrutinee to the local 'DsM' environment when
-- checking a case expression:
@@ -1031,51 +1059,9 @@ addScrutTmCs :: Maybe (LHsExpr GhcTc) -> [Id] -> DsM a -> DsM a
addScrutTmCs Nothing _ k = k
addScrutTmCs (Just scr) [x] k = do
scr_e <- dsLExpr scr
- locallyExtendPmDelta (\delta -> addPmCts delta (unitBag (PmCoreCt x scr_e))) k
+ locallyExtendPmDelta (\deltas -> addPmCtsDeltas deltas (unitBag (PmCoreCt x scr_e))) k
addScrutTmCs _ _ _ = panic "addScrutTmCs: HsCase with more than one case binder"
-addPmConCts :: Delta -> Id -> PmAltCon -> [TyVar] -> [EvVar] -> [Id] -> DsM (Maybe Delta)
-addPmConCts delta x con tvs dicts fields = runMaybeT $ do
- delta_ty <- MaybeT $ addPmCts delta (listToBag (PmTyCt . evVarPred <$> dicts))
- delta_tm_ty <- MaybeT $ addPmCts delta_ty (unitBag (PmConCt x con tvs fields))
- pure delta_tm_ty
-
--- | Add equalities to the local 'DsM' environment when checking the RHS of a
--- case expression:
--- case e of x { p1 -> e1; ... pn -> en }
--- When we go deeper to check e.g. e1 we record (x ~ p1).
-addPatTmCs :: [Pat GhcTc] -- LHS (should have length 1)
- -> [Id] -- MatchVars (should have length 1)
- -> DsM a
- -> DsM a
--- Computes an approximation of the Covered set for p1 (which pmCheck currently
--- discards).
-addPatTmCs ps xs k = do
- fam_insts <- dsGetFamInstEnvs
- grds <- concat <$> zipWithM (translatePat fam_insts) xs ps
- locallyExtendPmDelta (\delta -> computeCovered grds delta) k
-
--- | A dead simple version of 'pmCheck' that only computes the Covered set.
--- So it only cares about collecting positive info.
--- We use it to collect info from a pattern when we check its RHS.
--- See 'addPatTmCs'.
-computeCovered :: GrdVec -> Delta -> DsM (Maybe Delta)
--- The duplication with 'pmCheck' is really unfortunate, but it's simpler than
--- separating out the common cases with 'pmCheck', because that would make the
--- ConVar case harder to understand.
-computeCovered [] delta = pure (Just delta)
-computeCovered (PmLet { pm_id = x, pm_let_expr = e } : ps) delta = do
- delta' <- expectJust "x is fresh" <$> addPmCts delta (unitBag (PmCoreCt x e))
- computeCovered ps delta'
-computeCovered (PmBang{} : ps) delta = do
- computeCovered ps delta
-computeCovered (p : ps) delta
- | PmCon{ pm_id = x, pm_con_con = con, pm_con_tvs = tvs, pm_con_args = args
- , pm_con_dicts = dicts } <- p
- = addPmConCts delta x con tvs dicts args >>= \case
- Nothing -> pure Nothing
- Just delta' -> computeCovered ps delta'
-
{-
%************************************************************************
%* *
@@ -1114,7 +1100,7 @@ redundantAndInaccessibleRhss tree = (fromOL ol_red, fromOL ol_inacc)
-- even safely delete the equation without altering semantics)
-- See Note [Determining inaccessible clauses]
go :: AnnotatedTree -> (OrdList RhsInfo, OrdList RhsInfo, OrdList RhsInfo)
- go (AccessibleRhs info) = (unitOL info, nilOL, nilOL)
+ go (AccessibleRhs _ info) = (unitOL info, nilOL, nilOL)
go (InaccessibleRhs info) = (nilOL, nilOL, unitOL info) -- presumably redundant
go (MayDiverge t) = case go t of
-- See Note [Determining inaccessible clauses]