summaryrefslogtreecommitdiff
path: root/compiler/GHC/Tc/Solver/Monad.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Tc/Solver/Monad.hs')
-rw-r--r--compiler/GHC/Tc/Solver/Monad.hs243
1 files changed, 201 insertions, 42 deletions
diff --git a/compiler/GHC/Tc/Solver/Monad.hs b/compiler/GHC/Tc/Solver/Monad.hs
index 96228fcce5..9a4383a508 100644
--- a/compiler/GHC/Tc/Solver/Monad.hs
+++ b/compiler/GHC/Tc/Solver/Monad.hs
@@ -2,8 +2,10 @@
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}
@@ -42,7 +44,7 @@ module GHC.Tc.Solver.Monad (
newWantedNC, newWantedEvVarNC,
newDerivedNC,
newBoundEvVarId,
- unifyTyVar, reportUnifications,
+ unifyTyVar, reportUnifications, touchabilityTest, TouchabilityTestResult(..),
setEvBind, setWantedEq,
setWantedEvTerm, setEvBindIfWanted,
newEvVar, newGivenEvVar, newGivenEvVars,
@@ -113,7 +115,7 @@ module GHC.Tc.Solver.Monad (
-- if the whole instance matcher simply belongs
-- here
- breakTyVarCycle, rewriterView
+ breakTyVarCycle_maybe, rewriterView
) where
import GHC.Prelude
@@ -161,6 +163,7 @@ import GHC.Types.Unique.Supply
import GHC.Tc.Types
import GHC.Tc.Types.Origin
import GHC.Tc.Types.Constraint
+import GHC.Tc.Utils.Unify
import GHC.Core.Predicate
import GHC.Types.Unique.Set
@@ -169,7 +172,7 @@ import Control.Monad
import GHC.Utils.Monad
import Data.IORef
import GHC.Exts (oneShot)
-import Data.List ( mapAccumL )
+import Data.List ( mapAccumL, partition )
import Data.List.NonEmpty ( NonEmpty(..) )
import Control.Arrow ( first )
@@ -696,27 +699,31 @@ kickOutAfterFillingCoercionHole hole filled_co
holes_of_co = coercionHolesOfCo filled_co
kick_out :: InertCans -> (WorkList, InertCans)
- kick_out ics@(IC { inert_irreds = irreds })
- = let (to_kick, to_keep) = partitionBagWith kick_ct irreds
+ kick_out ics@(IC { inert_blocked = blocked })
+ = let (to_kick, to_keep) = partitionBagWith kick_ct blocked
kicked_out = extendWorkListCts (bagToList to_kick) emptyWorkList
- ics' = ics { inert_irreds = to_keep }
+ ics' = ics { inert_blocked = to_keep }
in
(kicked_out, ics')
kick_ct :: Ct -> Either Ct Ct
-- Left: kick out; Right: keep. But even if we keep, we may need
-- to update the set of blocking holes
- kick_ct ct@(CIrredCan { cc_status = BlockedCIS holes })
+ kick_ct ct@(CIrredCan { cc_reason = HoleBlockerReason holes })
| hole `elementOfUniqSet` holes
= let new_holes = holes `delOneFromUniqSet` hole
`unionUniqSets` holes_of_co
- updated_ct = ct { cc_status = BlockedCIS new_holes }
+ updated_ct = ct { cc_reason = HoleBlockerReason new_holes }
in
if isEmptyUniqSet new_holes
then Left updated_ct
else Right updated_ct
- kick_ct other = Right other
+
+ | otherwise
+ = Right ct
+
+ kick_ct other = pprPanic "kickOutAfterFillingCoercionHole" (ppr other)
--------------
addInertSafehask :: InertCans -> Ct -> InertCans
@@ -887,17 +894,21 @@ getUnsolvedInerts :: TcS ( Bag Implication
-- (because they come from the inert set)
-- the unsolved implics may not be
getUnsolvedInerts
- = do { IC { inert_eqs = tv_eqs
- , inert_funeqs = fun_eqs
- , inert_irreds = irreds
- , inert_dicts = idicts
+ = do { IC { inert_eqs = tv_eqs
+ , inert_funeqs = fun_eqs
+ , inert_irreds = irreds
+ , inert_blocked = blocked
+ , inert_dicts = idicts
} <- getInertCans
; let unsolved_tv_eqs = foldTyEqs add_if_unsolved tv_eqs emptyCts
unsolved_fun_eqs = foldFunEqs add_if_unsolveds fun_eqs emptyCts
unsolved_irreds = Bag.filterBag is_unsolved irreds
+ unsolved_blocked = blocked -- all blocked equalities are W/D
unsolved_dicts = foldDicts add_if_unsolved idicts emptyCts
- unsolved_others = unsolved_irreds `unionBags` unsolved_dicts
+ unsolved_others = unionManyBags [ unsolved_irreds
+ , unsolved_dicts
+ , unsolved_blocked ]
; implics <- getWorkListImplics
@@ -1009,7 +1020,7 @@ This is best understood by example.
where cbv = F a
The cbv is a cycle-breaker var which stands for F a. See
- Note [Type variable cycles in Givens] in GHC.Tc.Solver.Canonical.
+ Note [Type variable cycles] in GHC.Tc.Solver.Canonical.
This is just like case 6, and we say "no". Saying "no" here is
essential in getting the parser to type-check, with its use of DisambECP.
@@ -1327,7 +1338,7 @@ runTcSWithEvBinds = runTcSWithEvBinds' True
runTcSWithEvBinds' :: Bool -- ^ Restore type variable cycles afterwards?
-- Don't if you want to reuse the InertSet.
- -- See also Note [Type variable cycles in Givens]
+ -- See also Note [Type variable cycles]
-- in GHC.Tc.Solver.Canonical
-> EvBindsVar
-> TcS a
@@ -1624,6 +1635,82 @@ reportUnifications (TcS thing_inside)
; TcM.updTcRef (tcs_unified env) (+ n_unifs)
; return (n_unifs, res) }
+data TouchabilityTestResult
+ -- See Note [Solve by unification] in GHC.Tc.Solver.Interact
+ -- which points out that having TouchableSameLevel is just an optimisation;
+ -- we could manage with TouchableOuterLevel alone (suitably renamed)
+ = TouchableSameLevel
+ | TouchableOuterLevel [TcTyVar] -- Promote these
+ TcLevel -- ..to this level
+ | Untouchable
+
+instance Outputable TouchabilityTestResult where
+ ppr TouchableSameLevel = text "TouchableSameLevel"
+ ppr (TouchableOuterLevel tvs lvl) = text "TouchableOuterLevel" <> parens (ppr lvl <+> ppr tvs)
+ ppr Untouchable = text "Untouchable"
+
+touchabilityTest :: CtFlavour -> TcTyVar -> TcType -> TcS TouchabilityTestResult
+-- This is the key test for untouchability:
+-- See Note [Unification preconditions] in GHC.Tc.Utils.Unify
+-- and Note [Solve by unification] in GHC.Tc.Solver.Interact
+touchabilityTest flav tv1 rhs
+ | flav /= Given -- See Note [Do not unify Givens]
+ , MetaTv { mtv_tclvl = tv_lvl, mtv_info = info } <- tcTyVarDetails tv1
+ , canSolveByUnification info rhs
+ = do { ambient_lvl <- getTcLevel
+ ; given_eq_lvl <- getInnermostGivenEqLevel
+
+ ; if | tv_lvl `sameDepthAs` ambient_lvl
+ -> return TouchableSameLevel
+
+ | tv_lvl `deeperThanOrSame` given_eq_lvl -- No intervening given equalities
+ , all (does_not_escape tv_lvl) free_skols -- No skolem escapes
+ -> return (TouchableOuterLevel free_metas tv_lvl)
+
+ | otherwise
+ -> return Untouchable }
+ | otherwise
+ = return Untouchable
+ where
+ (free_metas, free_skols) = partition isPromotableMetaTyVar $
+ nonDetEltsUniqSet $
+ tyCoVarsOfType rhs
+
+ does_not_escape tv_lvl fv
+ | isTyVar fv = tv_lvl `deeperThanOrSame` tcTyVarLevel fv
+ | otherwise = True
+ -- Coercion variables are not an escape risk
+ -- If an implication binds a coercion variable, it'll have equalities,
+ -- so the "intervening given equalities" test above will catch it
+ -- Coercion holes get filled with coercions, so again no problem.
+
+{- Note [Do not unify Givens]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider this GADT match
+ data T a where
+ T1 :: T Int
+ ...
+
+ f x = case x of
+ T1 -> True
+ ...
+
+So we get f :: T alpha[1] -> beta[1]
+ x :: T alpha[1]
+and from the T1 branch we get the implication
+ forall[2] (alpha[1] ~ Int) => beta[1] ~ Bool
+
+Now, clearly we don't want to unify alpha:=Int! Yet at the moment we
+process [G] alpha[1] ~ Int, we don't have any given-equalities in the
+inert set, and hence there are no given equalities to make alpha untouchable.
+
+NB: if it were alpha[2] ~ Int, this argument wouldn't hold. But that
+never happens: invariant (GivenInv) in Note [TcLevel invariants]
+in GHC.Tc.Utils.TcType.
+
+Simple solution: never unify in Givens!
+-}
+
getDefaultInfo :: TcS ([Type], (Bool, Bool))
getDefaultInfo = wrapTcS TcM.tcGetDefaultTys
@@ -2130,51 +2217,123 @@ matchFamTcM tycon args
************************************************************************
-}
--- | Replace all type family applications in the RHS with fresh variables,
--- emitting givens that relate the type family application to the variable.
--- See Note [Type variable cycles in Givens] in GHC.Tc.Solver.Canonical.
-breakTyVarCycle :: CtLoc
- -> TcType -- the RHS
- -> TcS TcType -- new RHS that doesn't have any type families
--- This could be considerably more efficient. See Detail (5) of Note.
-breakTyVarCycle loc = go
+-- | Conditionally replace all type family applications in the RHS with fresh
+-- variables, emitting givens that relate the type family application to the
+-- variable. See Note [Type variable cycles] in GHC.Tc.Solver.Canonical.
+-- This only works under conditions as described in the Note; otherwise, returns
+-- Nothing.
+breakTyVarCycle_maybe :: CtEvidence
+ -> CheckTyEqResult -- result of checkTypeEq
+ -> CanEqLHS
+ -> TcType -- RHS
+ -> TcS (Maybe (TcTyVar, CoercionN, TcType))
+ -- new RHS that doesn't have any type families
+ -- co :: new type ~N old type
+ -- TcTyVar is the LHS tv; convenient for the caller
+breakTyVarCycle_maybe (ctLocOrigin . ctEvLoc -> CycleBreakerOrigin _) _ _ _
+ -- see Detail (7) of Note
+ = return Nothing
+
+breakTyVarCycle_maybe ev cte_result (TyVarLHS lhs_tv) rhs
+ | NomEq <- eq_rel
+
+ , cte_result `cterHasOnlyProblem` cteSolubleOccurs
+ -- only do this if the only problem is a soluble occurs-check
+ -- See Detail (8) of the Note.
+
+ = do { should_break <- final_check
+ ; if should_break then do { (co, new_rhs) <- go rhs
+ ; return (Just (lhs_tv, co, new_rhs)) }
+ else return Nothing }
where
+ flavour = ctEvFlavour ev
+ eq_rel = ctEvEqRel ev
+
+ final_check
+ | Given <- flavour
+ = return True
+ | ctFlavourContainsDerived flavour
+ = do { result <- touchabilityTest Derived lhs_tv rhs
+ ; return $ case result of
+ Untouchable -> False
+ _ -> True }
+ | otherwise
+ = return False
+
+ -- This could be considerably more efficient. See Detail (5) of Note.
+ go :: TcType -> TcS (CoercionN, TcType)
go ty | Just ty' <- rewriterView ty = go ty'
go (Rep.TyConApp tc tys)
- | isTypeFamilyTyCon tc
+ | isTypeFamilyTyCon tc -- worried about whether this type family is not actually
+ -- causing trouble? See Detail (5) of Note.
= do { let (fun_args, extra_args) = splitAt (tyConArity tc) tys
fun_app = mkTyConApp tc fun_args
fun_app_kind = tcTypeKind fun_app
- ; new_tv <- wrapTcS (TcM.newCycleBreakerTyVar fun_app_kind)
+ ; (co, new_ty) <- emit_work fun_app_kind fun_app
+ ; (extra_args_cos, extra_args') <- mapAndUnzipM go extra_args
+ ; return (mkAppCos co extra_args_cos, mkAppTys new_ty extra_args') }
+ -- Worried that this substitution will change kinds?
+ -- See Detail (3) of Note
+
+ | otherwise
+ = do { (cos, tys) <- mapAndUnzipM go tys
+ ; return (mkTyConAppCo Nominal tc cos, mkTyConApp tc tys) }
+
+ go (Rep.AppTy ty1 ty2)
+ = do { (co1, ty1') <- go ty1
+ ; (co2, ty2') <- go ty2
+ ; return (mkAppCo co1 co2, mkAppTy ty1' ty2') }
+ go (Rep.FunTy vis w arg res)
+ = do { (co_w, w') <- go w
+ ; (co_arg, arg') <- go arg
+ ; (co_res, res') <- go res
+ ; return (mkFunCo Nominal co_w co_arg co_res, mkFunTy vis w' arg' res') }
+ go (Rep.CastTy ty cast_co)
+ = do { (co, ty') <- go ty
+ -- co :: ty' ~N ty
+ -- return_co :: (ty' |> cast_co) ~ (ty |> cast_co)
+ ; return (castCoercionKind1 co Nominal ty' ty cast_co, mkCastTy ty' cast_co) }
+
+ go ty@(Rep.TyVarTy {}) = skip ty
+ go ty@(Rep.LitTy {}) = skip ty
+ go ty@(Rep.ForAllTy {}) = skip ty -- See Detail (1) of Note
+ go ty@(Rep.CoercionTy {}) = skip ty -- See Detail (2) of Note
+
+ skip ty = return (mkNomReflCo ty, ty)
+
+ emit_work :: TcKind -- of the function application
+ -> TcType -- original function application
+ -> TcS (CoercionN, TcType) -- rewritten type (the fresh tyvar)
+ emit_work fun_app_kind fun_app = case flavour of
+ Given ->
+ do { new_tv <- wrapTcS (TcM.newCycleBreakerTyVar fun_app_kind)
; let new_ty = mkTyVarTy new_tv
given_pred = mkHeteroPrimEqPred fun_app_kind fun_app_kind
fun_app new_ty
given_term = evCoercion $ mkNomReflCo new_ty -- See Detail (4) of Note
- ; new_given <- newGivenEvVar loc (given_pred, given_term)
- ; traceTcS "breakTyVarCycle replacing type family" (ppr new_given)
+ ; new_given <- newGivenEvVar new_loc (given_pred, given_term)
+ ; traceTcS "breakTyVarCycle replacing type family in Given" (ppr new_given)
; emitWorkNC [new_given]
; updInertTcS $ \is ->
is { inert_cycle_breakers = (new_tv, fun_app) :
inert_cycle_breakers is }
- ; extra_args' <- mapM go extra_args
- ; return (mkAppTys new_ty extra_args') }
- -- Worried that this substitution will change kinds?
- -- See Detail (3) of Note
+ ; return (mkNomReflCo new_ty, new_ty) }
+ -- Why reflexive? See Detail (4) of the Note
- | otherwise
- = mkTyConApp tc <$> mapM go tys
+ _derived_or_wd ->
+ do { new_tv <- wrapTcS (TcM.newFlexiTyVar fun_app_kind)
+ ; let new_ty = mkTyVarTy new_tv
+ ; co <- emitNewWantedEq new_loc Nominal new_ty fun_app
+ ; return (co, new_ty) }
- go (Rep.AppTy ty1 ty2) = mkAppTy <$> go ty1 <*> go ty2
- go (Rep.FunTy vis w arg res) = mkFunTy vis <$> go w <*> go arg <*> go res
- go (Rep.CastTy ty co) = mkCastTy <$> go ty <*> pure co
+ -- See Detail (7) of the Note
+ new_loc = updateCtLocOrigin (ctEvLoc ev) CycleBreakerOrigin
- go ty@(Rep.TyVarTy {}) = return ty
- go ty@(Rep.LitTy {}) = return ty
- go ty@(Rep.ForAllTy {}) = return ty -- See Detail (1) of Note
- go ty@(Rep.CoercionTy {}) = return ty -- See Detail (2) of Note
+-- does not fit scenario from Note
+breakTyVarCycle_maybe _ _ _ _ = return Nothing
-- | Fill in CycleBreakerTvs with the variables they stand for.
--- See Note [Type variable cycles in Givens] in GHC.Tc.Solver.Canonical.
+-- See Note [Type variable cycles] in GHC.Tc.Solver.Canonical.
restoreTyVarCycles :: InertSet -> TcM ()
restoreTyVarCycles is
= forM_ (inert_cycle_breakers is) $ \ (cycle_breaker_tv, orig_ty) ->