diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/GHC/CmmToLlvm/CodeGen.hs | 7 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline.hs | 2 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/Binds.hs | 5 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/Expr.hs | 11 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/GuardedRHSs.hs | 49 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/Match.hs | 57 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/Monad.hs | 16 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/PmCheck.hs | 148 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/PmCheck/Oracle.hs | 2 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/PmCheck/Types.hs | 22 | ||||
-rw-r--r-- | compiler/GHC/HsToCore/PmCheck/Types.hs-boot | 6 | ||||
-rw-r--r-- | compiler/typecheck/TcRnTypes.hs | 8 |
12 files changed, 177 insertions, 156 deletions
diff --git a/compiler/GHC/CmmToLlvm/CodeGen.hs b/compiler/GHC/CmmToLlvm/CodeGen.hs index 62ebeb9ba7..e01c6fe886 100644 --- a/compiler/GHC/CmmToLlvm/CodeGen.hs +++ b/compiler/GHC/CmmToLlvm/CodeGen.hs @@ -1482,17 +1482,20 @@ genMachOp_slow opt op [x, y] = case op of MO_FF_Conv _ _ -> panicOp MO_V_Insert {} -> panicOp - MO_V_Extract {} -> panicOp MO_VS_Neg {} -> panicOp MO_VF_Insert {} -> panicOp - MO_VF_Extract {} -> panicOp MO_VF_Neg {} -> panicOp MO_AlignmentCheck {} -> panicOp +#if __GLASGOW_HASKELL__ < 811 + MO_VF_Extract {} -> panicOp + MO_V_Extract {} -> panicOp +#endif + where binLlvmOp ty binOp = runExprData $ do vx <- exprToVarW x diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs index 3c31e34eb8..ca6d501be6 100644 --- a/compiler/GHC/Driver/Pipeline.hs +++ b/compiler/GHC/Driver/Pipeline.hs @@ -353,7 +353,9 @@ compileForeign hsc_env lang stub_c = do LangObjc -> Cobjc LangObjcxx -> Cobjcxx LangAsm -> As True -- allow CPP +#if __GLASGOW_HASKELL__ < 811 RawObject -> panic "compileForeign: should be unreachable" +#endif (_, stub_o, _) <- runPipeline StopLn hsc_env (stub_c, Nothing, Just (RealPhase phase)) Nothing (Temporary TFL_GhcSession) diff --git a/compiler/GHC/HsToCore/Binds.hs b/compiler/GHC/HsToCore/Binds.hs index 124427578d..66293cbb69 100644 --- a/compiler/GHC/HsToCore/Binds.hs +++ b/compiler/GHC/HsToCore/Binds.hs @@ -75,6 +75,7 @@ import UniqSet( nonDetEltsUniqSet ) import MonadUtils import qualified GHC.LanguageExtensions as LangExt import Control.Monad +import Data.List.NonEmpty ( nonEmpty ) {-********************************************************************** * * @@ -175,8 +176,8 @@ dsHsBind dflags b@(FunBind { fun_id = L _ fun dsHsBind dflags (PatBind { pat_lhs = pat, pat_rhs = grhss , pat_ext = NPatBindTc _ ty , pat_ticks = (rhs_tick, var_ticks) }) - = do { body_expr <- dsGuarded grhss ty - ; checkGuardMatches PatBindGuards grhss + = do { rhss_deltas <- checkGuardMatches PatBindGuards grhss + ; body_expr <- dsGuarded grhss ty (nonEmpty rhss_deltas) ; let body' = mkOptTickBox rhs_tick body_expr pat' = decideBangHood dflags pat ; (force_var,sel_binds) <- mkSelectorBinds var_ticks pat body' diff --git a/compiler/GHC/HsToCore/Expr.hs b/compiler/GHC/HsToCore/Expr.hs index b627d6e841..6328d473ec 100644 --- a/compiler/GHC/HsToCore/Expr.hs +++ b/compiler/GHC/HsToCore/Expr.hs @@ -69,6 +69,7 @@ import Outputable import PatSyn import Control.Monad +import Data.List.NonEmpty ( nonEmpty ) {- ************************************************************************ @@ -216,8 +217,8 @@ dsUnliftedBind (PatBind {pat_lhs = pat, pat_rhs = grhss , pat_ext = NPatBindTc _ ty }) body = -- let C x# y# = rhs in body -- ==> case rhs of C x# y# -> body - do { rhs <- dsGuarded grhss ty - ; checkGuardMatches PatBindGuards grhss + do { rhs_deltas <- checkGuardMatches PatBindGuards grhss + ; rhs <- dsGuarded grhss ty (nonEmpty rhs_deltas) ; let upat = unLoc pat eqn = EqnInfo { eqn_pats = [upat], eqn_orig = FromSource, @@ -446,9 +447,9 @@ dsExpr (HsMultiIf res_ty alts) = mkErrorExpr | otherwise - = do { match_result <- liftM (foldr1 combineMatchResults) - (mapM (dsGRHS IfAlt res_ty) alts) - ; checkGuardMatches IfAlt (GRHSs noExtField alts (noLoc emptyLocalBinds)) + = do { let grhss = GRHSs noExtField alts (noLoc emptyLocalBinds) + ; rhss_deltas <- checkGuardMatches IfAlt grhss + ; match_result <- dsGRHSs IfAlt grhss res_ty (nonEmpty rhss_deltas) ; error_expr <- mkErrorExpr ; extractMatchResult match_result error_expr } where diff --git a/compiler/GHC/HsToCore/GuardedRHSs.hs b/compiler/GHC/HsToCore/GuardedRHSs.hs index 37a7cd591b..0cb8f22848 100644 --- a/compiler/GHC/HsToCore/GuardedRHSs.hs +++ b/compiler/GHC/HsToCore/GuardedRHSs.hs @@ -9,7 +9,7 @@ Matching guarded right-hand-sides (GRHSs) {-# LANGUAGE CPP #-} {-# LANGUAGE ViewPatterns #-} -module GHC.HsToCore.GuardedRHSs ( dsGuarded, dsGRHSs, dsGRHS, isTrueLHsExpr ) where +module GHC.HsToCore.GuardedRHSs ( dsGuarded, dsGRHSs, isTrueLHsExpr ) where #include "HsVersions.h" @@ -23,15 +23,15 @@ import MkCore import CoreSyn import CoreUtils (bindNonRec) -import BasicTypes (Origin(FromSource)) -import GHC.Driver.Session -import GHC.HsToCore.PmCheck (needToRunPmCheck, addTyCsDs, addPatTmCs, addScrutTmCs) import GHC.HsToCore.Monad import GHC.HsToCore.Utils +import GHC.HsToCore.PmCheck.Types ( Deltas, initDeltas ) import Type ( Type ) import Util import SrcLoc import Outputable +import Control.Monad ( zipWithM ) +import Data.List.NonEmpty ( NonEmpty, toList ) {- @dsGuarded@ is used for pattern bindings. @@ -46,32 +46,38 @@ producing an expression with a runtime error in the corner if necessary. The type argument gives the type of the @ei@. -} -dsGuarded :: GRHSs GhcTc (LHsExpr GhcTc) -> Type -> DsM CoreExpr -dsGuarded grhss rhs_ty = do - match_result <- dsGRHSs PatBindRhs grhss rhs_ty +dsGuarded :: GRHSs GhcTc (LHsExpr GhcTc) -> Type -> Maybe (NonEmpty Deltas) -> DsM CoreExpr +dsGuarded grhss rhs_ty mb_rhss_deltas = do + match_result <- dsGRHSs PatBindRhs grhss rhs_ty mb_rhss_deltas error_expr <- mkErrorAppDs nON_EXHAUSTIVE_GUARDS_ERROR_ID rhs_ty empty extractMatchResult match_result error_expr -- In contrast, @dsGRHSs@ produces a @MatchResult@. dsGRHSs :: HsMatchContext GhcRn - -> GRHSs GhcTc (LHsExpr GhcTc) -- Guarded RHSs - -> Type -- Type of RHS + -> GRHSs GhcTc (LHsExpr GhcTc) -- ^ Guarded RHSs + -> Type -- ^ Type of RHS + -> Maybe (NonEmpty Deltas) -- ^ Refined pattern match checking + -- models, one for each GRHS. Defaults + -- to 'initDeltas' if 'Nothing'. -> DsM MatchResult -dsGRHSs hs_ctx (GRHSs _ grhss binds) rhs_ty +dsGRHSs hs_ctx (GRHSs _ grhss binds) rhs_ty mb_rhss_deltas = ASSERT( notNull grhss ) - do { match_results <- mapM (dsGRHS hs_ctx rhs_ty) grhss + do { match_results <- case toList <$> mb_rhss_deltas of + Nothing -> mapM (dsGRHS hs_ctx rhs_ty initDeltas) grhss + Just rhss_deltas -> ASSERT( length grhss == length rhss_deltas ) + zipWithM (dsGRHS hs_ctx rhs_ty) rhss_deltas grhss ; let match_result1 = foldr1 combineMatchResults match_results match_result2 = adjustMatchResultDs (dsLocalBinds binds) match_result1 -- NB: nested dsLet inside matchResult ; return match_result2 } -dsGRHSs _ (XGRHSs nec) _ = noExtCon nec +dsGRHSs _ (XGRHSs nec) _ _ = noExtCon nec -dsGRHS :: HsMatchContext GhcRn -> Type -> LGRHS GhcTc (LHsExpr GhcTc) +dsGRHS :: HsMatchContext GhcRn -> Type -> Deltas -> LGRHS GhcTc (LHsExpr GhcTc) -> DsM MatchResult -dsGRHS hs_ctx rhs_ty (L _ (GRHS _ guards rhs)) - = matchGuards (map unLoc guards) (PatGuard hs_ctx) rhs rhs_ty -dsGRHS _ _ (L _ (XGRHS nec)) = noExtCon nec +dsGRHS hs_ctx rhs_ty rhs_deltas (L _ (GRHS _ guards rhs)) + = updPmDeltas rhs_deltas (matchGuards (map unLoc guards) (PatGuard hs_ctx) rhs rhs_ty) +dsGRHS _ _ _ (L _ (XGRHS nec)) = noExtCon nec {- ************************************************************************ @@ -120,18 +126,9 @@ matchGuards (LetStmt _ binds : stmts) ctx rhs rhs_ty = do matchGuards (BindStmt _ pat bind_rhs _ _ : stmts) ctx rhs rhs_ty = do let upat = unLoc pat - dicts = collectEvVarsPat upat match_var <- selectMatchVar upat - dflags <- getDynFlags - match_result <- - -- See Note [Type and Term Equality Propagation] in Check - applyWhen (needToRunPmCheck dflags FromSource) - -- FromSource might not be accurate, but at worst - -- we do superfluous calls to the pattern match - -- oracle. - (addTyCsDs dicts . addScrutTmCs (Just bind_rhs) [match_var] . addPatTmCs [upat] [match_var]) - (matchGuards stmts ctx rhs rhs_ty) + match_result <- matchGuards stmts ctx rhs rhs_ty core_rhs <- dsLExpr bind_rhs match_result' <- matchSinglePatVar match_var (StmtCtxt ctx) pat rhs_ty match_result diff --git a/compiler/GHC/HsToCore/Match.hs b/compiler/GHC/HsToCore/Match.hs index 0542fd5e7e..3a795c5947 100644 --- a/compiler/GHC/HsToCore/Match.hs +++ b/compiler/GHC/HsToCore/Match.hs @@ -62,7 +62,7 @@ import FastString import Unique import UniqDFM -import Control.Monad( when, unless ) +import Control.Monad( unless ) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NEL import qualified Data.Map as Map @@ -742,36 +742,49 @@ matchWrapper ctxt mb_scr (MG { mg_alts = L _ matches [] -> mapM newSysLocalDsNoLP arg_tys (m:_) -> selectMatchVars (map unLoc (hsLMatchPats m)) - ; eqns_info <- mapM (mk_eqn_info new_vars) matches + -- Pattern match check warnings for /this match-group/. + -- @rhss_deltas@ is a flat list of covered Deltas for each RHS. + -- Each Match will split off one Deltas for its RHSs from this. + ; rhss_deltas <- if isMatchContextPmChecked dflags origin ctxt + then addScrutTmCs mb_scr new_vars $ + -- See Note [Type and Term Equality Propagation] + checkMatches (DsMatchContext ctxt locn) new_vars matches + else pure [] -- Ultimately this will result in passing Nothing + -- to dsGRHSs as match_deltas - -- Pattern match check warnings for /this match-group/ - ; when (isMatchContextPmChecked dflags origin ctxt) $ - addScrutTmCs mb_scr new_vars $ - -- See Note [Type and Term Equality Propagation] - checkMatches dflags (DsMatchContext ctxt locn) new_vars matches + ; eqns_info <- mk_eqn_infos matches rhss_deltas ; result_expr <- handleWarnings $ matchEquations ctxt new_vars eqns_info rhs_ty ; return (new_vars, result_expr) } where + -- rhss_deltas is a flat list, whereas there are multiple GRHSs per match. + -- mk_eqn_infos will thread rhss_deltas as state through calls to + -- mk_eqn_info, distributing each rhss_deltas to a GRHS. + mk_eqn_infos (L _ match : matches) rhss_deltas + = do { (info, rhss_deltas') <- mk_eqn_info match rhss_deltas + ; infos <- mk_eqn_infos matches rhss_deltas' + ; return (info:infos) } + mk_eqn_infos [] _ = return [] -- Called once per equation in the match, or alternative in the case - mk_eqn_info vars (L _ (Match { m_pats = pats, m_grhss = grhss })) + mk_eqn_info (Match { m_pats = pats, m_grhss = grhss }) rhss_deltas + | XGRHSs nec <- grhss = noExtCon nec + | GRHSs _ grhss' _ <- grhss, let n_grhss = length grhss' = do { dflags <- getDynFlags ; let upats = map (unLoc . decideBangHood dflags) pats - dicts = collectEvVarsPats upats - - ; match_result <- - -- Extend the environment with knowledge about - -- the matches before desugaring the RHS - -- See Note [Type and Term Equality Propagation] - applyWhen (needToRunPmCheck dflags origin) - (addTyCsDs dicts . addScrutTmCs mb_scr vars . addPatTmCs upats vars) - (dsGRHSs ctxt grhss rhs_ty) - - ; return (EqnInfo { eqn_pats = upats - , eqn_orig = FromSource - , eqn_rhs = match_result }) } - mk_eqn_info _ (L _ (XMatch nec)) = noExtCon nec + -- Split off one Deltas for each GRHS of the current Match from the + -- flat list of GRHS Deltas *for all matches* (see the call to + -- checkMatches above). + ; let (match_deltas, rhss_deltas') = splitAt n_grhss rhss_deltas + -- The list of Deltas is empty iff we don't perform any coverage + -- checking, in which case nonEmpty does the right thing by passing + -- Nothing. + ; match_result <- dsGRHSs ctxt grhss rhs_ty (NEL.nonEmpty match_deltas) + ; return ( EqnInfo { eqn_pats = upats + , eqn_orig = FromSource + , eqn_rhs = match_result } + , rhss_deltas' ) } + mk_eqn_info (XMatch nec) _ = noExtCon nec handleWarnings = if isGenerated origin then discardWarningsDs diff --git a/compiler/GHC/HsToCore/Monad.hs b/compiler/GHC/HsToCore/Monad.hs index b13a7f3304..df8833c8fe 100644 --- a/compiler/GHC/HsToCore/Monad.hs +++ b/compiler/GHC/HsToCore/Monad.hs @@ -30,7 +30,7 @@ module GHC.HsToCore.Monad ( DsMetaEnv, DsMetaVal(..), dsGetMetaEnv, dsLookupMetaEnv, dsExtendMetaEnv, -- Getting and setting pattern match oracle states - getPmDelta, updPmDelta, + getPmDeltas, updPmDeltas, -- Get COMPLETE sets of a TyCon dsGetCompleteMatches, @@ -282,7 +282,7 @@ mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var cc_st_var } lcl_env = DsLclEnv { dsl_meta = emptyNameEnv , dsl_loc = real_span - , dsl_delta = initDelta + , dsl_deltas = initDeltas } in (gbl_env, lcl_env) @@ -381,14 +381,14 @@ the @SrcSpan@ being carried around. getGhcModeDs :: DsM GhcMode getGhcModeDs = getDynFlags >>= return . ghcMode --- | Get the current pattern match oracle state. See 'dsl_delta'. -getPmDelta :: DsM Delta -getPmDelta = do { env <- getLclEnv; return (dsl_delta env) } +-- | Get the current pattern match oracle state. See 'dsl_deltas'. +getPmDeltas :: DsM Deltas +getPmDeltas = do { env <- getLclEnv; return (dsl_deltas env) } -- | Set the pattern match oracle state within the scope of the given action. --- See 'dsl_delta'. -updPmDelta :: Delta -> DsM a -> DsM a -updPmDelta delta = updLclEnv (\env -> env { dsl_delta = delta }) +-- See 'dsl_deltas'. +updPmDeltas :: Deltas -> DsM a -> DsM a +updPmDeltas delta = updLclEnv (\env -> env { dsl_deltas = delta }) getSrcSpanDs :: DsM SrcSpan getSrcSpanDs = do { env <- getLclEnv 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] diff --git a/compiler/GHC/HsToCore/PmCheck/Oracle.hs b/compiler/GHC/HsToCore/PmCheck/Oracle.hs index c0722249d8..2004c88d32 100644 --- a/compiler/GHC/HsToCore/PmCheck/Oracle.hs +++ b/compiler/GHC/HsToCore/PmCheck/Oracle.hs @@ -12,7 +12,7 @@ Authors: George Karachalias <george.karachalias@cs.kuleuven.be> module GHC.HsToCore.PmCheck.Oracle ( DsM, tracePm, mkPmId, - Delta, initDelta, lookupRefuts, lookupSolution, + Delta, initDeltas, lookupRefuts, lookupSolution, PmCt(PmTyCt), PmCts, pattern PmVarCt, pattern PmCoreCt, pattern PmConCt, pattern PmNotConCt, pattern PmBotCt, diff --git a/compiler/GHC/HsToCore/PmCheck/Types.hs b/compiler/GHC/HsToCore/PmCheck/Types.hs index 26e6ffc67e..e263a1640b 100644 --- a/compiler/GHC/HsToCore/PmCheck/Types.hs +++ b/compiler/GHC/HsToCore/PmCheck/Types.hs @@ -29,7 +29,8 @@ module GHC.HsToCore.PmCheck.Types ( setIndirectSDIE, setEntrySDIE, traverseSDIE, -- * The pattern match oracle - VarInfo(..), TmState(..), TyState(..), Delta(..), initDelta + VarInfo(..), TmState(..), TyState(..), Delta(..), + Deltas(..), initDeltas, liftDeltasM ) where #include "HsVersions.h" @@ -64,6 +65,7 @@ import Numeric (fromRat) import Data.Foldable (find) import qualified Data.List.NonEmpty as NonEmpty import Data.Ratio +import qualified Data.Semigroup as Semi -- | Literals (simple and overloaded ones) for pattern match checking. -- @@ -520,8 +522,7 @@ instance Outputable TyState where initTyState :: TyState initTyState = TySt emptyBag --- | Term and type constraints to accompany each value vector abstraction. --- For efficiency, we store the term oracle state instead of the term +-- | An inert set of canonical (i.e. mutually compatible) term and type -- constraints. data Delta = MkDelta { delta_ty_st :: TyState -- Type oracle; things like a~Int , delta_tm_st :: TmState } -- Term oracle; things like x~Nothing @@ -537,3 +538,18 @@ instance Outputable Delta where ppr (delta_tm_st delta), ppr (delta_ty_st delta) ] + +-- | A disjunctive bag of 'Delta's, representing a refinement type. +newtype Deltas = MkDeltas (Bag Delta) + +initDeltas :: Deltas +initDeltas = MkDeltas (unitBag initDelta) + +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) diff --git a/compiler/GHC/HsToCore/PmCheck/Types.hs-boot b/compiler/GHC/HsToCore/PmCheck/Types.hs-boot index beef421d46..abbaa33cfa 100644 --- a/compiler/GHC/HsToCore/PmCheck/Types.hs-boot +++ b/compiler/GHC/HsToCore/PmCheck/Types.hs-boot @@ -1,7 +1,9 @@ module GHC.HsToCore.PmCheck.Types where -import GhcPrelude () +import Bag data Delta -initDelta :: Delta +newtype Deltas = MkDeltas (Bag Delta) + +initDeltas :: Deltas diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs index e9da98379c..856dfa7f14 100644 --- a/compiler/typecheck/TcRnTypes.hs +++ b/compiler/typecheck/TcRnTypes.hs @@ -99,7 +99,7 @@ import TcOrigin import Annotations import InstEnv import FamInstEnv -import {-# SOURCE #-} GHC.HsToCore.PmCheck.Types (Delta) +import {-# SOURCE #-} GHC.HsToCore.PmCheck.Types (Deltas) import IOEnv import RdrName import Name @@ -317,9 +317,9 @@ data DsLclEnv = DsLclEnv { dsl_loc :: RealSrcSpan, -- To put in pattern-matching error msgs -- See Note [Note [Type and Term Equality Propagation] in Check.hs - -- The oracle state Delta is augmented as we walk inwards, - -- through each pattern match in turn - dsl_delta :: Delta + -- The set of reaching values Deltas is augmented as we walk inwards, + -- refined through each pattern match in turn + dsl_deltas :: Deltas } -- Inside [| |] brackets, the desugarer looks |