diff options
Diffstat (limited to 'compiler/GHC/HsToCore/PmCheck/Oracle.hs')
-rw-r--r-- | compiler/GHC/HsToCore/PmCheck/Oracle.hs | 538 |
1 files changed, 283 insertions, 255 deletions
diff --git a/compiler/GHC/HsToCore/PmCheck/Oracle.hs b/compiler/GHC/HsToCore/PmCheck/Oracle.hs index b3a6010b23..78238965fc 100644 --- a/compiler/GHC/HsToCore/PmCheck/Oracle.hs +++ b/compiler/GHC/HsToCore/PmCheck/Oracle.hs @@ -4,22 +4,25 @@ Authors: George Karachalias <george.karachalias@cs.kuleuven.be> Ryan Scott <ryan.gl.scott@gmail.com> -} -{-# LANGUAGE CPP, LambdaCase, TupleSections, PatternSynonyms, ViewPatterns, MultiWayIf #-} +{-# LANGUAGE CPP, LambdaCase, TupleSections, PatternSynonyms, ViewPatterns, MultiWayIf, ScopedTypeVariables #-} -- | The pattern match oracle. The main export of the module are the functions -- 'addPmCts' for adding facts to the oracle, and 'provideEvidence' to turn a --- 'Delta' into a concrete evidence for an equation. +-- 'Nabla' into a concrete evidence for an equation. +-- +-- In terms of the [Lower Your Guards paper](https://dl.acm.org/doi/abs/10.1145/3408989) +-- describing the implementation, this module is concerned with Sections 3.4, 3.6 and 3.7. +-- E.g., it represents refinement types diretly as a normalised refinement type 'Nabla'. module GHC.HsToCore.PmCheck.Oracle ( DsM, tracePm, mkPmId, - Delta, initDeltas, lookupRefuts, lookupSolution, + Nabla, initNablas, lookupRefuts, lookupSolution, PmCt(PmTyCt), PmCts, pattern PmVarCt, pattern PmCoreCt, pattern PmConCt, pattern PmNotConCt, pattern PmBotCt, pattern PmNotBotCt, addPmCts, -- Add a constraint to the oracle. - canDiverge, -- Try to add the term equality x ~ ⊥ provideEvidence ) where @@ -154,12 +157,7 @@ mkOneConFull arg_tys con = do -- to the type oracle let ty_cs = substTheta subst (eqSpecPreds eq_spec ++ thetas) -- Figure out the types of strict constructor fields - let arg_is_strict - | RealDataCon dc <- con - , isNewTyCon (dataConTyCon dc) - = [True] -- See Note [Divergence of Newtype matches] - | otherwise - = map isBanged $ conLikeImplBangs con + let arg_is_strict = map isBanged $ conLikeImplBangs con strict_arg_tys = filterByList arg_is_strict field_tys' return (ex_tvs, vars, listToBag ty_cs, strict_arg_tys) @@ -170,8 +168,8 @@ mkOneConFull arg_tys con = do ------------------------------------- -- * 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 +-- | Given a 'Nabla', check if it is compatible with new facts encoded in this +-- this check. If so, return 'Just' a potentially extended 'Nabla'. Return -- 'Nothing' if unsatisfiable. -- -- There are three essential SatisfiabilityChecks: @@ -180,22 +178,22 @@ mkOneConFull arg_tys con = do -- 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)) +newtype SatisfiabilityCheck = SC (Nabla -> DsM (Maybe Nabla)) --- | Check the given 'Delta' for satisfiability by the given --- 'SatisfiabilityCheck'. Return 'Just' a new, potentially extended, 'Delta' if +-- | Check the given 'Nabla' for satisfiability by the given +-- 'SatisfiabilityCheck'. Return 'Just' a new, potentially extended, 'Nabla' if -- successful, and 'Nothing' otherwise. -runSatisfiabilityCheck :: Delta -> SatisfiabilityCheck -> DsM (Maybe Delta) -runSatisfiabilityCheck delta (SC chk) = chk delta +runSatisfiabilityCheck :: Nabla -> SatisfiabilityCheck -> DsM (Maybe Nabla) +runSatisfiabilityCheck nabla (SC chk) = chk nabla -- | 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 + c nabla = a nabla >>= \case Nothing -> pure Nothing - Just delta' -> b delta' + Just nabla' -> b nabla' instance Monoid SatisfiabilityCheck where -- We only need this because of mconcat (which we use in place of sconcat, @@ -214,13 +212,13 @@ instance Monoid SatisfiabilityCheck where -- 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 + :: Nabla -- ^ The ambient term and type constraints -- (known to be satisfiable). -> Bag TyCt -- ^ The new type constraints. -> Bag TmCt -- ^ The new term constraints. -> [Type] -- ^ The strict argument types. - -> DsM (Maybe Delta) - -- ^ @'Just' delta@ if the constraints (@delta@) are + -> DsM (Maybe Nabla) + -- ^ @'Just' nabla@ if the constraints (@nabla@) are -- satisfiable, and each strict argument type is inhabitable. -- 'Nothing' otherwise. pmIsSatisfiable amb_cs new_ty_cs new_tm_cs strict_arg_tys = @@ -493,21 +491,21 @@ tyOracle (TySt inert) cts 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 +-- Returns a new 'Nabla' 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 PredType -> SatisfiabilityCheck -tyIsSatisfiable recheck_complete_sets new_ty_cs = SC $ \delta -> +tyIsSatisfiable recheck_complete_sets new_ty_cs = SC $ \nabla -> if isEmptyBag new_ty_cs - then pure (Just delta) - else tyOracle (delta_ty_st delta) new_ty_cs >>= \case + then pure (Just nabla) + else tyOracle (nabla_ty_st nabla) new_ty_cs >>= \case Nothing -> pure Nothing Just ty_st' -> do - let delta' = delta{ delta_ty_st = ty_st' } + let nabla' = nabla{ nabla_ty_st = ty_st' } if recheck_complete_sets - then ensureAllPossibleMatchesInhabited delta' - else pure (Just delta') + then ensureAllInhabited nabla' + else pure (Just nabla') {- ********************************************************************* @@ -619,21 +617,46 @@ warning messages (which can be alleviated by someone with enough dedication). -} -- | A 'SatisfiabilityCheck' based on new term-level constraints. --- Returns a new 'Delta' if the new constraints are compatible with existing +-- Returns a new 'Nabla' if the new constraints are compatible with existing -- ones. tmIsSatisfiable :: Bag TmCt -> SatisfiabilityCheck -tmIsSatisfiable new_tm_cs = SC $ \delta -> runMaybeT $ foldlM addTmCt delta new_tm_cs +tmIsSatisfiable new_tm_cs = SC $ \nabla -> runMaybeT $ foldlM addTmCt nabla new_tm_cs ----------------------- -- * Looking up VarInfo emptyVarInfo :: Id -> VarInfo -emptyVarInfo x = VI (idType x) [] emptyPmAltConSet NoPM +-- We could initialise @bot@ to @Just False@ in case of an unlifted type here, +-- but it's cleaner to let the user of the constraint solver take care of this. +-- After all, there are also strict fields, the unliftedness of which isn't +-- evident in the type. So treating unlifted types here would never be +-- sufficient anyway. +emptyVarInfo x = VI (idType x) [] emptyPmAltConSet MaybeBot NoPM lookupVarInfo :: TmState -> Id -> VarInfo -- (lookupVarInfo tms x) tells what we know about 'x' lookupVarInfo (TmSt env _) x = fromMaybe (emptyVarInfo x) (lookupSDIE env x) +-- | Like @lookupVarInfo ts x@, but @lookupVarInfo ts x = (y, vi)@ also looks +-- through newtype constructors. We have @x ~ N1 (... (Nk y))@ such that the +-- returned @y@ doesn't have a positive newtype constructor constraint +-- associated with it (yet). The 'VarInfo' returned is that of @y@'s +-- representative. +-- +-- Careful, this means that @idType x@ might be different to @idType y@, even +-- modulo type normalisation! +-- +-- See also Note [Coverage checking Newtype matches]. +lookupVarInfoNT :: TmState -> Id -> (Id, VarInfo) +lookupVarInfoNT ts x = case lookupVarInfo ts x of + VI{ vi_pos = as_newtype -> Just y } -> lookupVarInfoNT ts y + res -> (x, res) + where + as_newtype = listToMaybe . mapMaybe go + go (PmAltConLike (RealDataCon dc), _, [y]) + | isNewDataCon dc = Just y + go _ = Nothing + initPossibleMatches :: TyState -> VarInfo -> DsM VarInfo initPossibleMatches ty_st vi@VI{ vi_ty = ty, vi_cache = NoPM } = do -- New evidence might lead to refined info on ty, in turn leading to discovery @@ -670,13 +693,6 @@ initPossibleMatches ty_st vi@VI{ vi_ty = ty, vi_cache = NoPM } = do Just cs -> pure vi{ vi_ty = ty', vi_cache = PM (mkUniqDSet <$> cs) } 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_st = ts, delta_ty_st = ty_st } x - = initPossibleMatches ty_st (lookupVarInfo ts x) - {- Note [COMPLETE sets on data families] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ User-defined COMPLETE sets involving data families are attached to the family @@ -721,22 +737,11 @@ TyCon, so tc_rep = tc_fam afterwards. -} ------------------------------------------------ --- * Exported utility functions querying 'Delta' +-- * Exported utility functions querying 'Nabla' --- | Check whether adding a constraint @x ~ BOT@ to 'Delta' succeeds. -canDiverge :: Delta -> Id -> Bool -canDiverge delta@MkDelta{ delta_tm_st = ts } x - | VI _ pos neg _ <- lookupVarInfo ts x - = isEmptyPmAltConSet neg && all pos_can_diverge pos - where - pos_can_diverge (PmAltConLike (RealDataCon dc), _, [y]) - -- See Note [Divergence of Newtype matches] - | isNewTyCon (dataConTyCon dc) = canDiverge delta y - pos_can_diverge _ = False - -{- Note [Divergence of Newtype matches] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Newtypes behave rather strangely when compared to ordinary DataCons. In a +{- Note [Coverage checking Newtype matches] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Newtypes have quite peculiar match semantics compared to ordinary DataCons. In a pattern-match, they behave like a irrefutable (lazy) match, but for inhabitation testing purposes (e.g. at construction sites), they behave rather like a DataCon with a *strict* field, because they don't contribute their own bottom and are @@ -751,19 +756,21 @@ This distinction becomes apparent in #17248: If we treat Newtypes like we treat regular DataCons, we would mark the third clause as redundant, which clearly is unsound. The solution: -1. When compiling the PmCon guard in 'pmCompileTree', don't add a @DivergeIf@, - because the match will never diverge. -2. Regard @T2 x@ as 'canDiverge' iff @x@ 'canDiverge'. E.g. @T2 x ~ _|_@ <=> - @x ~ _|_@. This way, the third clause will still be marked as inaccessible - RHS instead of redundant. -3. When testing for inhabitants ('mkOneConFull'), we regard the newtype field as - strict, so that the newtype is inhabited iff its field is inhabited. +1. 'isPmAltConMatchStrict' returns False for newtypes, indicating that a + newtype match is lazy. +2. When we find @x ~ T2 y@, transfer all constraints on @x@ (which involve @⊥@) + to @y@, similar to what 'equate' does, and don't add a @x /~ ⊥@ constraint. + This way, the third clause will still be marked as inaccessible RHS instead + of redundant. This is ensured by calling 'lookupVarInfoNT'. +3. Immediately reject when we find @x /~ T2@. +Handling of Newtypes is also described in the Appendix of the Lower Your Guards paper, +where you can find the solution in a perhaps more digestible format. -} -lookupRefuts :: Uniquable k => Delta -> k -> [PmAltCon] +lookupRefuts :: Uniquable k => Nabla -> k -> [PmAltCon] -- Unfortunately we need the extra bit of polymorphism and the unfortunate -- duplication of lookupVarInfo here. -lookupRefuts MkDelta{ delta_tm_st = ts@(TmSt (SDIE env) _) } k = +lookupRefuts MkNabla{ nabla_tm_st = ts@(TmSt (SDIE env) _) } k = case lookupUDFM_Directly env (getUnique k) of Nothing -> [] Just (Indirect y) -> pmAltConSetElems (vi_neg (lookupVarInfo ts y)) @@ -773,10 +780,10 @@ isDataConSolution :: (PmAltCon, [TyVar], [Id]) -> Bool isDataConSolution (PmAltConLike (RealDataCon _), _, _) = True isDataConSolution _ = False --- @lookupSolution delta x@ picks a single solution ('vi_pos') of @x@ from +-- @lookupSolution nabla x@ picks a single solution ('vi_pos') of @x@ from -- possibly many, preferring 'RealDataCon' solutions whenever possible. -lookupSolution :: Delta -> Id -> Maybe (PmAltCon, [TyVar], [Id]) -lookupSolution delta x = case vi_pos (lookupVarInfo (delta_tm_st delta) x) of +lookupSolution :: Nabla -> Id -> Maybe (PmAltCon, [TyVar], [Id]) +lookupSolution nabla x = case vi_pos (lookupVarInfo (nabla_tm_st nabla) x) of [] -> Nothing pos | Just sol <- find isDataConSolution pos -> Just sol @@ -843,13 +850,13 @@ instance Outputable PmCt where ppr (PmTyCt pred_ty) = ppr pred_ty ppr (PmTmCt tm_ct) = ppr tm_ct --- | Adds new constraints to 'Delta' and returns 'Nothing' if that leads to a +-- | Adds new constraints to 'Nabla' and returns 'Nothing' if that leads to a -- contradiction. -addPmCts :: Delta -> PmCts -> DsM (Maybe Delta) +addPmCts :: Nabla -> PmCts -> DsM (Maybe Nabla) -- See Note [TmState invariants]. -addPmCts delta cts = do +addPmCts nabla cts = do let (ty_cts, tm_cts) = partitionTyTmCts cts - runSatisfiabilityCheck delta $ mconcat + runSatisfiabilityCheck nabla $ mconcat [ tyIsSatisfiable True (listToBag ty_cts) , tmIsSatisfiable (listToBag tm_cts) ] @@ -862,44 +869,40 @@ partitionTyTmCts = partitionEithers . map to_either . toList -- | Adds a single term constraint by dispatching to the various term oracle -- functions. -addTmCt :: Delta -> TmCt -> MaybeT DsM Delta -addTmCt delta (TmVarCt x y) = addVarCt delta x y -addTmCt delta (TmCoreCt x e) = addCoreCt delta x e -addTmCt delta (TmConCt x con tvs args) = addConCt delta x con tvs args -addTmCt delta (TmNotConCt x con) = addNotConCt delta x con -addTmCt delta (TmBotCt x) = addBotCt delta x -addTmCt delta (TmNotBotCt x) = addNotBotCt delta x +addTmCt :: Nabla -> TmCt -> MaybeT DsM Nabla +addTmCt nabla (TmVarCt x y) = addVarCt nabla x y +addTmCt nabla (TmCoreCt x e) = addCoreCt nabla x e +addTmCt nabla (TmConCt x con tvs args) = addConCt nabla x con tvs args +addTmCt nabla (TmNotConCt x con) = addNotConCt nabla x con +addTmCt nabla (TmBotCt x) = addBotCt nabla x +addTmCt nabla (TmNotBotCt x) = addNotBotCt nabla x -- | Adds the constraint @x ~ ⊥@, e.g. that evaluation of a particular 'Id' @x@ --- surely diverges. --- --- Only that's a lie, because we don't currently preserve the fact in 'Delta' --- after we checked compatibility. See Note [Preserving TmBotCt] -addBotCt :: Delta -> Id -> MaybeT DsM Delta -addBotCt delta x - | canDiverge delta x = pure delta - | otherwise = mzero - -{- Note [Preserving TmBotCt] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Whenever we add a new constraint to 'Delta' via 'addTmCt', we want to check it -for compatibility with existing constraints in the modeled indert set and then -add it the constraint itself to the inert set. -For a 'TmBotCt' @x ~ ⊥@ we don't actually add it to the inert set after checking -it for compatibility with 'Delta'. -And that is fine in the context of the patter-match checking algorithm! -Whenever we add a 'TmBotCt' (we only do so for checking divergence of bang -patterns and strict constructor matches), we don't add any more constraints to -the inert set afterwards, so we don't need to preserve it. --} +-- surely diverges. Quite similar to 'addConCt', only that it only cares about +-- ⊥. +addBotCt :: Nabla -> Id -> MaybeT DsM Nabla +addBotCt nabla@MkNabla{ nabla_tm_st = TmSt env reps } x = do + let (y, vi@VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x + case bot of + IsNotBot -> mzero -- There was x /~ ⊥. Contradiction! + IsBot -> pure nabla -- There already is x ~ ⊥. Nothing left to do + MaybeBot -> do -- We add x ~ ⊥ + let vi' = vi{ vi_bot = IsBot } + pure nabla{ nabla_tm_st = TmSt (setEntrySDIE env y vi') reps} -- | Record a @x ~/ K@ constraint, e.g. that a particular 'Id' @x@ can't --- take the shape of a 'PmAltCon' @K@ in the 'Delta' and return @Nothing@ if +-- take the shape of a 'PmAltCon' @K@ in the 'Nabla' and return @Nothing@ if -- that leads to a contradiction. -- See Note [TmState invariants]. -addNotConCt :: Delta -> Id -> PmAltCon -> MaybeT DsM Delta -addNotConCt delta@MkDelta{ delta_tm_st = TmSt env reps } x nalt = do - vi@(VI _ pos neg pm) <- lift (initLookupVarInfo delta x) +addNotConCt :: Nabla -> Id -> PmAltCon -> MaybeT DsM Nabla +addNotConCt _ _ (PmAltConLike (RealDataCon dc)) + | isNewDataCon dc = mzero -- (3) in Note [Coverage checking Newtype matches] +addNotConCt nabla@MkNabla{ nabla_tm_st = ts@(TmSt env reps) } x nalt = do + -- For good performance, it's important to initPossibleMatches here. + -- Otherwise we can't mark nalt as matched later on, incurring unnecessary + -- inhabitation tests for nalt. + vi@(VI _ pos neg _ pm) <- lift $ initPossibleMatches (nabla_ty_st nabla) + (lookupVarInfo ts x) -- 1. Bail out quickly when nalt contradicts a solution let contradicts nalt (cl, _tvs, _args) = eqPmAltCon cl nalt == Equal guard (not (any (contradicts nalt) pos)) @@ -911,13 +914,14 @@ addNotConCt delta@MkDelta{ delta_tm_st = TmSt env reps } x nalt = do -- See Note [Completeness checking with required Thetas] | hasRequiredTheta nalt = neg | otherwise = extendPmAltConSet neg nalt - let vi_ext = vi{ vi_neg = neg' } + MASSERT( isPmAltConMatchStrict nalt ) + let vi1 = vi{ vi_neg = neg', vi_bot = IsNotBot } -- 3. Make sure there's at least one other possible constructor - vi' <- case nalt of + vi2 <- case nalt of PmAltConLike cl - -> MaybeT (ensureInhabited delta vi_ext{ vi_cache = markMatched cl pm }) - _ -> pure vi_ext - pure delta{ delta_tm_st = TmSt (setEntrySDIE env x vi') reps } + -> ensureInhabited nabla vi1{ vi_cache = markMatched cl pm } + _ -> pure vi1 + pure nabla{ nabla_tm_st = TmSt (setEntrySDIE env x vi2) reps } hasRequiredTheta :: PmAltCon -> Bool hasRequiredTheta (PmAltConLike cl) = notNull req_theta @@ -980,100 +984,104 @@ guessConLikeUnivTyArgsFromResTy _ res_ty (PatSynCon ps) = do subst <- tcMatchTy con_res_ty res_ty traverse (lookupTyVar subst) univ_tvs --- | Adds the constraint @x ~/ ⊥@ to 'Delta'. +-- | Adds the constraint @x ~/ ⊥@ to 'Nabla'. Quite similar to 'addNotConCt', +-- but only cares for the ⊥ "constructor". +addNotBotCt :: Nabla -> Id -> MaybeT DsM Nabla +addNotBotCt nabla@MkNabla{ nabla_tm_st = TmSt env reps } x = do + let (y, vi@VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x + case bot of + IsBot -> mzero -- There was x ~ ⊥. Contradiction! + IsNotBot -> pure nabla -- There already is x /~ ⊥. Nothing left to do + MaybeBot -> do -- We add x /~ ⊥ and test if x is still inhabited + vi <- ensureInhabited nabla vi{ vi_bot = IsNotBot } + pure nabla{ nabla_tm_st = TmSt (setEntrySDIE env y vi) reps} + +-- | Returns (Just vi) if at least one member of each ConLike in the COMPLETE +-- set satisfies the oracle -- --- But doesn't really commit to upholding that constraint in the future. This --- will be rectified in a follow-up patch. The status quo should work good --- enough for now. -addNotBotCt :: Delta -> Id -> MaybeT DsM Delta -addNotBotCt delta@MkDelta{ delta_tm_st = TmSt env reps } x = do - vi <- lift $ initLookupVarInfo delta x - vi' <- MaybeT $ ensureInhabited delta vi - -- vi' has probably constructed and then thinned out some PossibleMatches. - -- We want to cache that work - pure delta{ delta_tm_st = TmSt (setEntrySDIE env x vi') reps} - -ensureInhabited :: Delta -> VarInfo -> DsM (Maybe VarInfo) - -- Returns (Just vi) if 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 +-- 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 :: Nabla -> VarInfo -> MaybeT DsM VarInfo +ensureInhabited nabla vi = case vi_bot vi of + MaybeBot -> pure vi -- The |-Bot rule from the paper + IsBot -> pure vi + IsNotBot -> lift (initPossibleMatches (nabla_ty_st nabla) vi) >>= inst_complete_sets where - set_cache vi cache = vi { vi_cache = cache } - - test NoPM = pure (Just NoPM) - test (PM ms) = runMaybeT (PM <$> 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 + -- | This is the |-Inst rule from the paper (section 4.5). Tries to + -- find an inhabitant in every complete set by instantiating with one their + -- constructors. If there is any complete set where we can't find an + -- inhabitant, the whole thing is uninhabited. + inst_complete_sets :: VarInfo -> MaybeT DsM VarInfo + inst_complete_sets vi@VI{ vi_cache = NoPM } = pure vi + inst_complete_sets vi@VI{ vi_cache = PM ms } = do + ms <- traverse (\cs -> inst_complete_set vi cs (uniqDSetToList cs)) ms + pure vi{ vi_cache = PM ms } + + inst_complete_set :: VarInfo -> ConLikeSet -> [ConLike] -> MaybeT DsM ConLikeSet + -- (inst_complete_set 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 + inst_complete_set _ _ [] = mzero + inst_complete_set vi cs (con:cons) = lift (inst_and_test vi con) >>= \case True -> pure cs - False -> find_one_inh (delOneFromUniqDSet cs con) cons + False -> inst_complete_set vi (delOneFromUniqDSet cs con) cons - inh_test :: ConLike -> DsM Bool - -- @inh_test K@ Returns False if a non-bottom value @v::ty@ cannot possibly + inst_and_test :: VarInfo -> ConLike -> DsM Bool + -- @inst_and_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 + -- the facts in Nabla into account. + inst_and_test vi con = do env <- dsGetFamInstEnvs case guessConLikeUnivTyArgsFromResTy env (vi_ty vi) con of Nothing -> pure True -- be conservative about this Just arg_tys -> do (_tvs, _vars, ty_cs, strict_arg_tys) <- mkOneConFull arg_tys con - tracePm "inh_test" (ppr con $$ ppr ty_cs) + tracePm "inst_and_test" (ppr con $$ ppr ty_cs) -- No need to run the term oracle compared to pmIsSatisfiable - fmap isJust <$> runSatisfiabilityCheck delta $ mconcat + fmap isJust <$> runSatisfiabilityCheck nabla $ mconcat -- Important to pass False to tyIsSatisfiable here, so that we won't - -- recursively call ensureAllPossibleMatchesInhabited, leading to an + -- recursively call ensureAllInhabited, leading to an -- endless recursion. [ tyIsSatisfiable False ty_cs , 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'. +-- 'vi_cache', considering the current type information in 'Nabla'. -- 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_st = TmSt env reps } - = runMaybeT (set_tm_cs_env delta <$> traverseSDIE go env) +ensureAllInhabited :: Nabla -> DsM (Maybe Nabla) +ensureAllInhabited nabla@MkNabla{ nabla_tm_st = TmSt env reps } + = runMaybeT (set_tm_cs_env nabla <$> traverseSDIE go env) where - set_tm_cs_env delta env = delta{ delta_tm_st = TmSt env reps } - go vi = MaybeT $ - initPossibleMatches (delta_ty_st delta) vi >>= ensureInhabited delta + set_tm_cs_env nabla env = nabla{ nabla_tm_st = TmSt env reps } + go vi = ensureInhabited nabla vi -------------------------------------- -- * Term oracle unification procedure -- | Adds a @x ~ y@ constraint by trying to unify two 'Id's and record the --- gained knowledge in 'Delta'. +-- gained knowledge in 'Nabla'. -- --- Returns @Nothing@ when there's a contradiction. Returns @Just delta@ --- when the constraint was compatible with prior facts, in which case @delta@ +-- Returns @Nothing@ when there's a contradiction. Returns @Just nabla@ +-- when the constraint was compatible with prior facts, in which case @nabla@ -- has integrated the knowledge from the equality constraint. -- -- See Note [TmState invariants]. -addVarCt :: Delta -> Id -> Id -> MaybeT DsM Delta -addVarCt delta@MkDelta{ delta_tm_st = TmSt env _ } x y +addVarCt :: Nabla -> Id -> Id -> MaybeT DsM Nabla +addVarCt nabla@MkNabla{ nabla_tm_st = TmSt 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@. - | sameRepresentativeSDIE env x y = pure delta - | otherwise = equate delta x y + | sameRepresentativeSDIE env x y = pure nabla + | otherwise = equate nabla x y -- | @equate ts@(TmSt env) x y@ merges the equivalence classes of @x@ and @y@ by -- adding an indirection to the environment. @@ -1082,12 +1090,12 @@ addVarCt delta@MkDelta{ delta_tm_st = TmSt env _ } x y -- Preconditions: @not (sameRepresentativeSDIE env x y)@ -- -- See Note [TmState invariants]. -equate :: Delta -> Id -> Id -> MaybeT DsM Delta -equate delta@MkDelta{ delta_tm_st = TmSt env reps } x y +equate :: Nabla -> Id -> Id -> MaybeT DsM Nabla +equate nabla@MkNabla{ nabla_tm_st = TmSt env reps } x y = ASSERT( not (sameRepresentativeSDIE env x y) ) case (lookupSDIE env x, lookupSDIE env y) of - (Nothing, _) -> pure (delta{ delta_tm_st = TmSt (setIndirectSDIE env x y) reps }) - (_, Nothing) -> pure (delta{ delta_tm_st = TmSt (setIndirectSDIE env y x) reps }) + (Nothing, _) -> pure (nabla{ nabla_tm_st = TmSt (setIndirectSDIE env x y) reps }) + (_, Nothing) -> pure (nabla{ nabla_tm_st = TmSt (setIndirectSDIE env y x) reps }) -- 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... @@ -1097,16 +1105,16 @@ equate delta@MkDelta{ delta_tm_st = TmSt env reps } x y let env_ind = setIndirectSDIE env x y -- Then sum up the refinement counters let env_refs = setEntrySDIE env_ind y vi_y - let delta_refs = delta{ delta_tm_st = TmSt env_refs reps } + let nabla_refs = nabla{ nabla_tm_st = TmSt env_refs reps } -- and then gradually merge every positive fact we have on x into y - let add_fact delta (cl, tvs, args) = addConCt delta y cl tvs args - delta_pos <- foldlM add_fact delta_refs (vi_pos vi_x) + let add_fact nabla (cl, tvs, args) = addConCt nabla y cl tvs args + nabla_pos <- foldlM add_fact nabla_refs (vi_pos vi_x) -- Do the same for negative info - let add_refut delta nalt = addNotConCt delta y nalt - delta_neg <- foldlM add_refut delta_pos (pmAltConSetElems (vi_neg vi_x)) + let add_refut nabla nalt = addNotConCt nabla y nalt + nabla_neg <- foldlM add_refut nabla_pos (pmAltConSetElems (vi_neg vi_x)) -- vi_cache will be updated in addNotConCt, so we are good to -- go! - pure delta_neg + pure nabla_neg -- | Add a @x ~ K tvs args ts@ constraint. -- @addConCt x K tvs args ts@ extends the substitution with a solution @@ -1114,9 +1122,9 @@ equate delta@MkDelta{ delta_tm_st = TmSt env reps } x y -- have on @x@, reject (@Nothing@) otherwise. -- -- See Note [TmState invariants]. -addConCt :: Delta -> Id -> PmAltCon -> [TyVar] -> [Id] -> MaybeT DsM Delta -addConCt delta@MkDelta{ delta_tm_st = TmSt env reps } x alt tvs args = do - VI ty pos neg cache <- lift (initLookupVarInfo delta x) +addConCt :: Nabla -> Id -> PmAltCon -> [TyVar] -> [Id] -> MaybeT DsM Nabla +addConCt nabla@MkNabla{ nabla_tm_st = ts@(TmSt env reps) } x alt tvs args = do + let VI ty pos neg bot cache = lookupVarInfo ts x -- First try to refute with a negative fact guard (not (elemPmAltConSet alt neg)) -- Then see if any of the other solutions (remember: each of them is an @@ -1132,10 +1140,19 @@ addConCt delta@MkDelta{ delta_tm_st = TmSt env reps } x alt tvs args = do when (length args /= length other_args) $ lift $ tracePm "error" (ppr x <+> ppr alt <+> ppr args <+> ppr other_args) let tm_cts = zipWithEqual "addConCt" PmVarCt args other_args - MaybeT $ addPmCts delta (listToBag ty_cts `unionBags` listToBag tm_cts) + MaybeT $ addPmCts nabla (listToBag ty_cts `unionBags` listToBag tm_cts) Nothing -> do let pos' = (alt, tvs, args):pos - pure delta{ delta_tm_st = TmSt (setEntrySDIE env x (VI ty pos' neg cache)) reps} + let nabla_with bot = nabla{ nabla_tm_st = TmSt (setEntrySDIE env x (VI ty pos' neg bot cache)) reps} + -- Do (2) in Note [Coverage checking Newtype matches] + case (alt, args) of + (PmAltConLike (RealDataCon dc), [y]) | isNewDataCon dc -> + case bot of + MaybeBot -> pure (nabla_with MaybeBot) + IsBot -> addBotCt (nabla_with MaybeBot) y + IsNotBot -> addNotBotCt (nabla_with MaybeBot) y + _ -> ASSERT( isPmAltConMatchStrict alt ) + pure (nabla_with IsNotBot) -- strict match ==> not ⊥ equateTys :: [Type] -> [Type] -> [PmCt] equateTys ts us = @@ -1184,9 +1201,9 @@ mkInhabitationCandidate x dc = do -- 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 +inhabitationCandidates :: Nabla -> Type -> DsM (Either Type (TyCon, Id, [InhabitationCandidate])) -inhabitationCandidates MkDelta{ delta_ty_st = ty_st } ty = do +inhabitationCandidates MkNabla{ nabla_ty_st = ty_st } ty = do pmTopNormaliseType ty_st ty >>= \case NoChange _ -> alts_to_check ty ty [] NormalisedByConstraints ty' -> alts_to_check ty' ty' [] @@ -1282,20 +1299,20 @@ we do the following: -- | 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'. +-- Returns the old 'Nabla' if all the types are non-void according to 'Nabla'. tysAreNonVoid :: RecTcChecker -> [Type] -> SatisfiabilityCheck -tysAreNonVoid rec_env strict_arg_tys = SC $ \delta -> do - all_non_void <- checkAllNonVoid rec_env delta strict_arg_tys +tysAreNonVoid rec_env strict_arg_tys = SC $ \nabla -> do + all_non_void <- checkAllNonVoid rec_env nabla strict_arg_tys -- Check if each strict argument type is inhabitable pure $ if all_non_void - then Just delta + then Just nabla else Nothing -- | Implements two performance optimizations, as described in -- @Note [Strict argument type constraints]@. -checkAllNonVoid :: RecTcChecker -> Delta -> [Type] -> DsM Bool +checkAllNonVoid :: RecTcChecker -> Nabla -> [Type] -> DsM Bool checkAllNonVoid rec_ts amb_cs strict_arg_tys = do - let definitely_inhabited = definitelyInhabitedType (delta_ty_st amb_cs) + let definitely_inhabited = definitelyInhabitedType (nabla_ty_st amb_cs) tys_to_check <- filterOutM definitely_inhabited strict_arg_tys -- See Note [Fuel for the inhabitation test] let rec_max_bound | tys_to_check `lengthExceeds` 1 @@ -1310,7 +1327,7 @@ checkAllNonVoid rec_ts amb_cs strict_arg_tys = do -- See @Note [Strict argument type constraints]@. nonVoid :: RecTcChecker -- ^ The per-'TyCon' recursion depth limit. - -> Delta -- ^ The ambient term/type constraints (known to be + -> Nabla -- ^ 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 @@ -1338,7 +1355,7 @@ nonVoid rec_ts amb_cs strict_arg_ty = do -- check if recursion is detected). -- -- See Note [Strict argument type constraints] - cand_is_inhabitable :: RecTcChecker -> Delta + cand_is_inhabitable :: RecTcChecker -> Nabla -> InhabitationCandidate -> DsM Bool cand_is_inhabitable rec_ts amb_cs (InhabitationCandidate{ ic_cs = new_cs @@ -1517,21 +1534,21 @@ on a list of strict argument types, we filter out all of the DI ones. -} -------------------------------------------- --- * Providing positive evidence for a Delta +-- * Providing positive evidence for a Nabla --- | @provideEvidence vs n delta@ returns a list of --- at most @n@ (but perhaps empty) refinements of @delta@ that instantiate +-- | @provideEvidence vs n nabla@ returns a list of +-- at most @n@ (but perhaps empty) refinements of @nabla@ that instantiate -- @vs@ to compatible constructor applications or wildcards. -- Negative information is only retained if literals are involved or when -- for recursive GADTs. -provideEvidence :: [Id] -> Int -> Delta -> DsM [Delta] +provideEvidence :: [Id] -> Int -> Nabla -> DsM [Nabla] provideEvidence = go where go _ 0 _ = pure [] - go [] _ delta = pure [delta] - go (x:xs) n delta = do - tracePm "provideEvidence" (ppr x $$ ppr xs $$ ppr delta $$ ppr n) - VI _ pos neg _ <- initLookupVarInfo delta x + go [] _ nabla = pure [nabla] + go (x:xs) n nabla = do + tracePm "provideEvidence" (ppr x $$ ppr xs $$ ppr nabla $$ ppr n) + let VI _ pos neg _ _ = lookupVarInfo (nabla_tm_st nabla) x case pos of _:_ -> do -- All solutions must be valid at once. Try to find candidates for their @@ -1544,56 +1561,58 @@ provideEvidence = go -- 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, _tvs, args) -> args) pos - go (arg_vas ++ xs) n delta + go (arg_vas ++ xs) n nabla [] -- When there are literals involved, just print negative info -- instead of listing missed constructors | notNull [ l | PmAltLit l <- pmAltConSetElems neg ] - -> go xs n delta - [] -> try_instantiate x xs n delta + -> go xs n nabla + [] -> try_instantiate x xs n nabla -- | Tries to instantiate a variable by possibly following the chain of -- newtypes and then instantiating to all ConLikes of the wrapped type's -- minimal residual COMPLETE set. - try_instantiate :: Id -> [Id] -> Int -> Delta -> DsM [Delta] + try_instantiate :: Id -> [Id] -> Int -> Nabla -> DsM [Nabla] -- Convention: x binds the outer constructor in the chain, y the inner one. - try_instantiate x xs n delta = do - (_src_ty, dcs, core_ty) <- tntrGuts <$> pmTopNormaliseType (delta_ty_st delta) (idType x) - let build_newtype (x, delta) (_ty, dc, arg_ty) = do + try_instantiate x xs n nabla = do + (_src_ty, dcs, core_ty) <- tntrGuts <$> pmTopNormaliseType (nabla_ty_st nabla) (idType x) + let build_newtype (x, nabla) (_ty, dc, arg_ty) = do y <- lift $ mkPmId arg_ty -- Newtypes don't have existentials (yet?!), so passing an empty -- list as ex_tvs. - delta' <- addConCt delta x (PmAltConLike (RealDataCon dc)) [] [y] - pure (y, delta') - runMaybeT (foldlM build_newtype (x, delta) dcs) >>= \case + nabla' <- addConCt nabla x (PmAltConLike (RealDataCon dc)) [] [y] + pure (y, nabla') + runMaybeT (foldlM build_newtype (x, nabla) dcs) >>= \case Nothing -> pure [] - Just (y, newty_delta) -> do + Just (y, newty_nabla) -> do -- Pick a COMPLETE set and instantiate it (n at max). Take care of ⊥. - pm <- vi_cache <$> initLookupVarInfo newty_delta y - mb_cls <- pickMinimalCompleteSet newty_delta pm + let vi = lookupVarInfo (nabla_tm_st newty_nabla) y + vi <- initPossibleMatches (nabla_ty_st newty_nabla) vi + mb_cls <- pickMinimalCompleteSet newty_nabla (vi_cache vi) case uniqDSetToList <$> mb_cls of - Just cls@(_:_) -> instantiate_cons y core_ty xs n newty_delta cls - Just [] | not (canDiverge newty_delta y) -> pure [] - -- Either ⊥ is still possible (think Void) or there are no COMPLETE - -- sets available, so we can assume it's inhabited - _ -> go xs n newty_delta - - instantiate_cons :: Id -> Type -> [Id] -> Int -> Delta -> [ConLike] -> DsM [Delta] + Just cls -> do + nablas <- instantiate_cons y core_ty xs n newty_nabla cls + if null nablas && vi_bot vi /= IsNotBot + then go xs n newty_nabla -- bot is still possible. Display a wildcard! + else pure nablas + Nothing -> go xs n newty_nabla -- no COMPLETE sets ==> inhabited + + instantiate_cons :: Id -> Type -> [Id] -> Int -> Nabla -> [ConLike] -> DsM [Nabla] instantiate_cons _ _ _ _ _ [] = pure [] instantiate_cons _ _ _ 0 _ _ = pure [] - instantiate_cons _ ty xs n delta _ + instantiate_cons _ ty xs n nabla _ -- We don't want to expose users to GHC-specific constructors for Int etc. | fmap (isTyConTriviallyInhabited . fst) (splitTyConApp_maybe ty) == Just True - = go xs n delta - instantiate_cons x ty xs n delta (cl:cls) = do + = go xs n nabla + instantiate_cons x ty xs n nabla (cl:cls) = do env <- dsGetFamInstEnvs case guessConLikeUnivTyArgsFromResTy env ty cl of - Nothing -> pure [delta] -- No idea how to refine this one, so just finish off with a wildcard + Nothing -> pure [nabla] -- No idea how to refine this one, so just finish off with a wildcard Just arg_tys -> do (tvs, arg_vars, new_ty_cs, strict_arg_tys) <- mkOneConFull arg_tys cl let new_tm_cs = unitBag (TmConCt x (PmAltConLike cl) tvs arg_vars) -- Now check satifiability - mb_delta <- pmIsSatisfiable delta new_ty_cs new_tm_cs strict_arg_tys + mb_nabla <- pmIsSatisfiable nabla new_ty_cs new_tm_cs strict_arg_tys tracePm "instantiate_cons" (vcat [ ppr x , ppr (idType x) , ppr ty @@ -1602,21 +1621,21 @@ provideEvidence = go , ppr new_tm_cs , ppr new_ty_cs , ppr strict_arg_tys - , ppr delta - , ppr mb_delta + , ppr nabla + , ppr mb_nabla , ppr n ]) - con_deltas <- case mb_delta of + con_nablas <- case mb_nabla of Nothing -> pure [] -- NB: We don't prepend arg_vars as we don't have any evidence on -- them and we only want to split once on a data type. They are -- inhabited, otherwise pmIsSatisfiable would have refuted. - Just delta' -> go xs n delta' - other_cons_deltas <- instantiate_cons x ty xs (n - length con_deltas) delta cls - pure (con_deltas ++ other_cons_deltas) + Just nabla' -> go xs n nabla' + other_cons_nablas <- instantiate_cons x ty xs (n - length con_nablas) nabla cls + pure (con_nablas ++ other_cons_nablas) -pickMinimalCompleteSet :: Delta -> PossibleMatches -> DsM (Maybe ConLikeSet) +pickMinimalCompleteSet :: Nabla -> PossibleMatches -> DsM (Maybe ConLikeSet) pickMinimalCompleteSet _ NoPM = pure Nothing --- TODO: First prune sets with type info in delta. But this is good enough for +-- TODO: First prune sets with type info in nabla. But this is good enough for -- now and less costly. See #17386. pickMinimalCompleteSet _ (PM clss) = do tracePm "pickMinimalCompleteSet" (ppr $ NonEmpty.toList clss) @@ -1626,14 +1645,14 @@ pickMinimalCompleteSet _ (PM clss) = do -- Which is the @x@ of a @let x = e'@ constraint (with @e@ semantically -- equivalent to @e'@) we encountered earlier, or a fresh identifier if -- there weren't any such constraints. -representCoreExpr :: Delta -> CoreExpr -> DsM (Delta, Id) -representCoreExpr delta@MkDelta{ delta_tm_st = ts@TmSt{ ts_reps = reps } } e - | Just rep <- lookupCoreMap reps e = pure (delta, rep) +representCoreExpr :: Nabla -> CoreExpr -> DsM (Nabla, Id) +representCoreExpr nabla@MkNabla{ nabla_tm_st = ts@TmSt{ ts_reps = reps } } e + | Just rep <- lookupCoreMap reps e = pure (nabla, rep) | otherwise = do rep <- mkPmId (exprType e) let reps' = extendCoreMap reps e rep - let delta' = delta{ delta_tm_st = ts{ ts_reps = reps' } } - pure (delta', rep) + let nabla' = nabla{ nabla_tm_st = ts{ ts_reps = reps' } } + pure (nabla', rep) -- | Inspects a 'PmCoreCt' @let x = e@ by recording constraints for @x@ based -- on the shape of the 'CoreExpr' @e@. Examples: @@ -1647,16 +1666,16 @@ representCoreExpr delta@MkDelta{ delta_tm_st = ts@TmSt{ ts_reps = reps } } e -- for other literals. See 'coreExprAsPmLit'. -- * Finally, if we have @let x = e@ and we already have seen @let y = e@, we -- want to record @x ~ y@. -addCoreCt :: Delta -> Id -> CoreExpr -> MaybeT DsM Delta -addCoreCt delta x e = do +addCoreCt :: Nabla -> Id -> CoreExpr -> MaybeT DsM Nabla +addCoreCt nabla x e = do simpl_opts <- initSimpleOpts <$> getDynFlags let e' = simpleOptExpr simpl_opts e - lift $ tracePm "addCoreCt" (ppr x <+> dcolon <+> ppr (idType x) $$ ppr e $$ ppr e') - execStateT (core_expr x e') delta + -- lift $ tracePm "addCoreCt" (ppr x <+> dcolon <+> ppr (idType x) $$ ppr e $$ ppr e') + execStateT (core_expr x e') nabla 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) () + core_expr :: Id -> CoreExpr -> StateT Nabla (MaybeT DsM) () -- TODO: Handle newtypes properly, by wrapping the expression in a DataCon -- This is the right thing for casts involving data family instances and -- their representation TyCon, though (which are not visible in source @@ -1681,7 +1700,7 @@ addCoreCt delta x e = do -- See Note [Detecting pattern synonym applications in expressions] | Var y <- e, Nothing <- isDataConId_maybe x -- We don't consider DataCons flexible variables - = modifyT (\delta -> addVarCt delta x y) + = modifyT (\nabla -> addVarCt nabla x y) | otherwise -- Any other expression. Try to find other uses of a semantically -- equivalent expression and represent them by the same variable! @@ -1699,13 +1718,13 @@ addCoreCt delta x e = do -- see if we already encountered a constraint @let y = e'@ with @e'@ -- semantically equivalent to @e@, in which case we may add the constraint -- @x ~ y@. - equate_with_similar_expr :: Id -> CoreExpr -> StateT Delta (MaybeT DsM) () + equate_with_similar_expr :: Id -> CoreExpr -> StateT Nabla (MaybeT DsM) () equate_with_similar_expr x e = do - rep <- StateT $ \delta -> swap <$> lift (representCoreExpr delta e) + rep <- StateT $ \nabla -> swap <$> lift (representCoreExpr nabla e) -- Note that @rep == x@ if we encountered @e@ for the first time. - modifyT (\delta -> addVarCt delta x rep) + modifyT (\nabla -> addVarCt nabla x rep) - bind_expr :: CoreExpr -> StateT Delta (MaybeT DsM) Id + bind_expr :: CoreExpr -> StateT Nabla (MaybeT DsM) Id bind_expr e = do x <- lift (lift (mkPmId (exprType e))) core_expr x e @@ -1713,10 +1732,12 @@ addCoreCt delta x e = do -- | Look at @let x = K taus theta es@ and generate the following -- constraints (assuming universals were dropped from @taus@ before): - -- 1. @a_1 ~ tau_1, ..., a_n ~ tau_n@ for fresh @a_i@ - -- 2. @y_1 ~ e_1, ..., y_m ~ e_m@ for fresh @y_i@ - -- 3. @x ~ K as ys@ - data_con_app :: Id -> InScopeSet -> DataCon -> [CoreExpr] -> StateT Delta (MaybeT DsM) () + -- 1. @x /~ ⊥@ if 'K' is not a Newtype constructor. + -- 2. @a_1 ~ tau_1, ..., a_n ~ tau_n@ for fresh @a_i@ + -- 3. @y_1 ~ e_1, ..., y_m ~ e_m@ for fresh @y_i@ + -- 4. @x ~ K as ys@ + -- This is quite similar to PmCheck.pmConCts. + data_con_app :: Id -> InScopeSet -> DataCon -> [CoreExpr] -> StateT Nabla (MaybeT DsM) () data_con_app x in_scope dc args = do let dc_ex_tvs = dataConExTyCoVars dc arty = dataConSourceArity dc @@ -1726,20 +1747,27 @@ addCoreCt delta x e = do uniq_supply <- lift $ lift $ getUniqueSupplyM let (_, ex_tvs) = cloneTyVarBndrs (mkEmptyTCvSubst in_scope) dc_ex_tvs uniq_supply ty_cts = equateTys (map mkTyVarTy ex_tvs) ex_tys - -- 1. @a_1 ~ tau_1, ..., a_n ~ tau_n@ for fresh @a_i@. See also #17703 - modifyT $ \delta -> MaybeT $ addPmCts delta (listToBag ty_cts) - -- 2. @y_1 ~ e_1, ..., y_m ~ e_m@ for fresh @y_i@ + -- 1. @x /~ ⊥@ if 'K' is not a Newtype constructor (#18341) + when (not (isNewDataCon dc)) $ + modifyT $ \nabla -> addNotBotCt nabla x + -- 2. @a_1 ~ tau_1, ..., a_n ~ tau_n@ for fresh @a_i@. See also #17703 + modifyT $ \nabla -> MaybeT $ addPmCts nabla (listToBag ty_cts) + -- 3. @y_1 ~ e_1, ..., y_m ~ e_m@ for fresh @y_i@ arg_ids <- traverse bind_expr vis_args - -- 3. @x ~ K as ys@ + -- 4. @x ~ K as ys@ pm_alt_con_app x (PmAltConLike (RealDataCon dc)) ex_tvs arg_ids -- | Adds a literal constraint, i.e. @x ~ 42@. - pm_lit :: Id -> PmLit -> StateT Delta (MaybeT DsM) () - pm_lit x lit = pm_alt_con_app x (PmAltLit lit) [] [] + -- Also we assume that literal expressions won't diverge, so this + -- will add a @x ~/ ⊥@ constraint. + pm_lit :: Id -> PmLit -> StateT Nabla (MaybeT DsM) () + pm_lit x lit = do + modifyT $ \nabla -> addNotBotCt nabla x + pm_alt_con_app x (PmAltLit lit) [] [] -- | Adds the given constructor application as a solution for @x@. - pm_alt_con_app :: Id -> PmAltCon -> [TyVar] -> [Id] -> StateT Delta (MaybeT DsM) () - pm_alt_con_app x con tvs args = modifyT $ \delta -> addConCt delta x con tvs args + pm_alt_con_app :: Id -> PmAltCon -> [TyVar] -> [Id] -> StateT Nabla (MaybeT DsM) () + pm_alt_con_app x con tvs args = modifyT $ \nabla -> addConCt nabla x con tvs args -- | Like 'modify', but with an effectful modifier action modifyT :: Monad m => (s -> m s) -> StateT s m () |