summaryrefslogtreecommitdiff
path: root/compiler/GHC/Tc/Solver/Rewrite.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Tc/Solver/Rewrite.hs')
-rw-r--r--compiler/GHC/Tc/Solver/Rewrite.hs195
1 files changed, 77 insertions, 118 deletions
diff --git a/compiler/GHC/Tc/Solver/Rewrite.hs b/compiler/GHC/Tc/Solver/Rewrite.hs
index b7573e7f09..6e8baf15a6 100644
--- a/compiler/GHC/Tc/Solver/Rewrite.hs
+++ b/compiler/GHC/Tc/Solver/Rewrite.hs
@@ -5,7 +5,7 @@
{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}
module GHC.Tc.Solver.Rewrite(
- rewrite, rewriteKind, rewriteArgsNom,
+ rewrite, rewriteArgsNom,
rewriteType
) where
@@ -34,15 +34,14 @@ import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Utils.Panic.Plain
import GHC.Tc.Solver.Monad as TcS
-import GHC.Tc.Solver.Types
+
import GHC.Utils.Misc
import GHC.Data.Maybe
import GHC.Exts (oneShot)
import Control.Monad
-import GHC.Utils.Monad ( zipWith3M )
-import Data.List.NonEmpty ( NonEmpty(..) )
import Control.Applicative (liftA3)
import GHC.Builtin.Types.Prim (tYPETyCon)
+import Data.List ( find )
{-
************************************************************************
@@ -82,18 +81,23 @@ liftTcS thing_inside
-- convenient wrapper when you have a CtEvidence describing
-- the rewriting operation
-runRewriteCtEv :: CtEvidence -> RewriteM a -> TcS a
+runRewriteCtEv :: CtEvidence -> RewriteM a -> TcS (a, RewriterSet)
runRewriteCtEv ev
= runRewrite (ctEvLoc ev) (ctEvFlavour ev) (ctEvEqRel ev)
-- Run thing_inside (which does the rewriting)
-runRewrite :: CtLoc -> CtFlavour -> EqRel -> RewriteM a -> TcS a
+-- Also returns the set of Wanteds which rewrote a Wanted;
+-- See Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint
+runRewrite :: CtLoc -> CtFlavour -> EqRel -> RewriteM a -> TcS (a, RewriterSet)
runRewrite loc flav eq_rel thing_inside
- = runRewriteM thing_inside fmode
- where
- fmode = FE { fe_loc = loc
- , fe_flavour = flav
- , fe_eq_rel = eq_rel }
+ = do { rewriters_ref <- newTcRef emptyRewriterSet
+ ; let fmode = RE { re_loc = loc
+ , re_flavour = flav
+ , re_eq_rel = eq_rel
+ , re_rewriters = rewriters_ref }
+ ; res <- runRewriteM thing_inside fmode
+ ; rewriters <- readTcRef rewriters_ref
+ ; return (res, rewriters) }
traceRewriteM :: String -> SDoc -> RewriteM ()
traceRewriteM herald doc = liftTcS $ traceTcS herald doc
@@ -108,13 +112,13 @@ getRewriteEnvField accessor
= mkRewriteM $ \env -> return (accessor env)
getEqRel :: RewriteM EqRel
-getEqRel = getRewriteEnvField fe_eq_rel
+getEqRel = getRewriteEnvField re_eq_rel
getRole :: RewriteM Role
getRole = eqRelRole <$> getEqRel
getFlavour :: RewriteM CtFlavour
-getFlavour = getRewriteEnvField fe_flavour
+getFlavour = getRewriteEnvField re_flavour
getFlavourRole :: RewriteM CtFlavourRole
getFlavourRole
@@ -123,7 +127,7 @@ getFlavourRole
; return (flavour, eq_rel) }
getLoc :: RewriteM CtLoc
-getLoc = getRewriteEnvField fe_loc
+getLoc = getRewriteEnvField re_loc
checkStackDepth :: Type -> RewriteM ()
checkStackDepth ty
@@ -134,38 +138,32 @@ checkStackDepth ty
setEqRel :: EqRel -> RewriteM a -> RewriteM a
setEqRel new_eq_rel thing_inside
= mkRewriteM $ \env ->
- if new_eq_rel == fe_eq_rel env
+ if new_eq_rel == re_eq_rel env
then runRewriteM thing_inside env
- else runRewriteM thing_inside (env { fe_eq_rel = new_eq_rel })
+ else runRewriteM thing_inside (env { re_eq_rel = new_eq_rel })
{-# INLINE setEqRel #-}
--- | Make sure that rewriting actually produces a coercion (in other
--- words, make sure our flavour is not Derived)
--- Note [No derived kind equalities]
-noBogusCoercions :: RewriteM a -> RewriteM a
-noBogusCoercions thing_inside
- = mkRewriteM $ \env ->
- -- No new thunk is made if the flavour hasn't changed (note the bang).
- let !env' = case fe_flavour env of
- Derived -> env { fe_flavour = Wanted WDeriv }
- _ -> env
- in
- runRewriteM thing_inside env'
-
bumpDepth :: RewriteM a -> RewriteM a
bumpDepth (RewriteM thing_inside)
= mkRewriteM $ \env -> do
-- bumpDepth can be called a lot during rewriting so we force the
-- new env to avoid accumulating thunks.
- { let !env' = env { fe_loc = bumpCtLocDepth (fe_loc env) }
+ { let !env' = env { re_loc = bumpCtLocDepth (re_loc env) }
; thing_inside env' }
+-- See Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint
+-- Precondition: the CtEvidence is a CtWanted of an equality
+recordRewriter :: CtEvidence -> RewriteM ()
+recordRewriter (CtWanted { ctev_dest = HoleDest hole })
+ = RewriteM $ \env -> updTcRef (re_rewriters env) (`addRewriterSet` hole)
+recordRewriter other = pprPanic "recordRewriter" (ppr other)
+
{-
Note [Rewriter EqRels]
~~~~~~~~~~~~~~~~~~~~~~~
When rewriting, we need to know which equality relation -- nominal
or representation -- we should be respecting. The only difference is
-that we rewrite variables by representational equalities when fe_eq_rel
+that we rewrite variables by representational equalities when re_eq_rel
is ReprEq, and that we unwrap newtypes when rewriting w.r.t.
representational equality.
@@ -203,14 +201,6 @@ soon throw out the phantoms when decomposing a TyConApp. (Or, the
canonicaliser will emit an insoluble, in which case we get
a better error message anyway.)
-Note [No derived kind equalities]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A kind-level coercion can appear in types, via mkCastTy. So, whenever
-we are generating a coercion in a dependent context (in other words,
-in a kind) we need to make sure that our flavour is never Derived
-(as Derived constraints have no evidence). The noBogusCoercions function
-changes the flavour from Derived just for this purpose.
-
-}
{- *********************************************************************
@@ -221,32 +211,21 @@ changes the flavour from Derived just for this purpose.
-}
-- | See Note [Rewriting].
--- If (xi, co) <- rewrite mode ev ty, then co :: xi ~r ty
+-- If (xi, co, rewriters) <- rewrite mode ev ty, then co :: xi ~r ty
-- where r is the role in @ev@.
+-- rewriters is the set of coercion holes that have been used to rewrite
+-- See Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint
rewrite :: CtEvidence -> TcType
- -> TcS Reduction
+ -> TcS (Reduction, RewriterSet)
rewrite ev ty
= do { traceTcS "rewrite {" (ppr ty)
- ; redn <- runRewriteCtEv ev (rewrite_one ty)
+ ; result@(redn, _) <- runRewriteCtEv ev (rewrite_one ty)
; traceTcS "rewrite }" (ppr $ reductionReducedType redn)
- ; return redn }
-
--- specialized to rewriting kinds: never Derived, always Nominal
--- See Note [No derived kind equalities]
--- See Note [Rewriting]
-rewriteKind :: CtLoc -> CtFlavour -> TcType -> TcS ReductionN
-rewriteKind loc flav ty
- = do { traceTcS "rewriteKind {" (ppr flav <+> ppr ty)
- ; let flav' = case flav of
- Derived -> Wanted WDeriv -- the WDeriv/WOnly choice matters not
- _ -> flav
- ; redn <- runRewrite loc flav' NomEq (rewrite_one ty)
- ; traceTcS "rewriteKind }" (ppr redn) -- the coercion inside the reduction is never a panic
- ; return redn }
+ ; return result }
-- See Note [Rewriting]
rewriteArgsNom :: CtEvidence -> TyCon -> [TcType]
- -> TcS Reductions
+ -> TcS (Reductions, RewriterSet)
-- Externally-callable, hence runRewrite
-- Rewrite a vector of types all at once; in fact they are
-- always the arguments of type family or class, so
@@ -255,15 +234,15 @@ rewriteArgsNom :: CtEvidence -> TyCon -> [TcType]
-- The kind passed in is the kind of the type family or class, call it T
-- The kind of T args must be constant (i.e. not depend on the args)
--
--- For Derived constraints the returned coercion may be undefined
--- because rewriting may use a Derived equality ([D] a ~ ty)
+-- Final return value returned which Wanteds rewrote another Wanted
+-- See Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint
rewriteArgsNom ev tc tys
= do { traceTcS "rewrite_args {" (vcat (map ppr tys))
- ; ArgsReductions redns@(Reductions _ tys') kind_co
+ ; (ArgsReductions redns@(Reductions _ tys') kind_co, rewriters)
<- runRewriteCtEv ev (rewrite_args_tc tc Nothing tys)
; massert (isReflMCo kind_co)
; traceTcS "rewrite }" (vcat (map ppr tys'))
- ; return redns }
+ ; return (redns, rewriters) }
-- | Rewrite a type w.r.t. nominal equality. This is useful to rewrite
-- a type w.r.t. any givens. It does not do type-family reduction. This
@@ -271,10 +250,10 @@ rewriteArgsNom ev tc tys
-- only givens.
rewriteType :: CtLoc -> TcType -> TcS TcType
rewriteType loc ty
- = do { redn <- runRewrite loc Given NomEq $
- rewrite_one ty
+ = do { (redn, _) <- runRewrite loc Given NomEq $
+ rewrite_one ty
-- use Given flavor so that it is rewritten
- -- only w.r.t. Givens, never Wanteds/Deriveds
+ -- only w.r.t. Givens, never Wanteds
-- (Shouldn't matter, if only Givens are present
-- anyway)
; return $ reductionReducedType redn }
@@ -462,38 +441,20 @@ rewrite_args_slow :: [TyCoBinder] -> Kind -> TcTyCoVarSet
-> [Role] -> [Type]
-> RewriteM ArgsReductions
rewrite_args_slow binders inner_ki fvs roles tys
--- Arguments used dependently must be rewritten with proper coercions, but
--- we're not guaranteed to get a proper coercion when rewriting with the
--- "Derived" flavour. So we must call noBogusCoercions when rewriting arguments
--- corresponding to binders that are dependent. However, we might legitimately
--- have *more* arguments than binders, in the case that the inner_ki is a variable
--- that gets instantiated with a Π-type. We conservatively choose not to produce
--- bogus coercions for these, too. Note that this might miss an opportunity for
--- a Derived rewriting a Derived. The solution would be to generate evidence for
--- Deriveds, thus avoiding this whole noBogusCoercions idea. See also
--- Note [No derived kind equalities]
- = do { rewritten_args <- zipWith3M rw (map isNamedBinder binders ++ repeat True)
- roles tys
- ; return $ simplifyArgsWorker binders inner_ki fvs roles rewritten_args }
+ = do { rewritten_args <- zipWithM rw roles tys
+ ; return (simplifyArgsWorker binders inner_ki fvs roles rewritten_args) }
where
{-# INLINE rw #-}
- rw :: Bool -- must we ensure to produce a real coercion here?
- -- see comment at top of function
- -> Role -> Type -> RewriteM Reduction
- rw True r ty = noBogusCoercions $ rw1 r ty
- rw False r ty = rw1 r ty
-
- {-# INLINE rw1 #-}
- rw1 :: Role -> Type -> RewriteM Reduction
- rw1 Nominal ty
+ rw :: Role -> Type -> RewriteM Reduction
+ rw Nominal ty
= setEqRel NomEq $
rewrite_one ty
- rw1 Representational ty
+ rw Representational ty
= setEqRel ReprEq $
rewrite_one ty
- rw1 Phantom ty
+ rw Phantom ty
-- See Note [Phantoms in the rewriter]
= do { ty <- liftTcS $ zonkTcType ty
; return $ mkReflRedn Phantom ty }
@@ -859,17 +820,13 @@ rewrite_exact_fam_app tc tys
where reduced = mkTyConApp tc xis
-- STEP 3: try the inerts
- ; result2 <- liftTcS $ lookupFamAppInert tc xis
; flavour <- getFlavour
+ ; result2 <- liftTcS $ lookupFamAppInert (`eqCanRewriteFR` (flavour, eq_rel)) tc xis
; case result2 of
- { Just (redn, fr@(_, inert_eq_rel))
-
- | fr `eqCanRewriteFR` (flavour, eq_rel) ->
- do { traceRewriteM "rewrite family application with inert" $
- vcat [ ppr tc <+> ppr xis
- , ppUnless (flavour == Derived) (ppr redn) ]
- -- Deriveds have no evidence, so we can't print the reduction
- ; finish True (homogenise downgraded_redn) }
+ { Just (redn, (inert_flavour, inert_eq_rel))
+ -> do { traceRewriteM "rewrite family application with inert"
+ (ppr tc <+> ppr xis $$ ppr redn)
+ ; finish (inert_flavour == Given) (homogenise downgraded_redn) }
-- this will sometimes duplicate an inert in the cache,
-- but avoiding doing so had no impact on performance, and
-- it seems easier not to weed out that special case
@@ -890,18 +847,17 @@ rewrite_exact_fam_app tc tys
-- call this if the above attempts made progress.
-- This recursively rewrites the result and then adds to the cache
finish :: Bool -- add to the cache?
+ -- Precondition: True ==> input coercion has
+ -- no coercion holes
-> Reduction -> RewriteM Reduction
finish use_cache redn
= do { -- rewrite the result: FINISH 1
final_redn <- rewrite_reduction redn
; eq_rel <- getEqRel
- ; flavour <- getFlavour
-- extend the cache: FINISH 2
- ; when (use_cache && eq_rel == NomEq && flavour /= Derived) $
+ ; when (use_cache && eq_rel == NomEq) $
-- the cache only wants Nominal eqs
- -- and Wanteds can rewrite Deriveds; the cache
- -- has only Givens
liftTcS $ extendFamAppCache tc tys final_redn
; return final_redn }
{-# INLINE finish #-}
@@ -1034,32 +990,35 @@ rewrite_tyvar2 :: TcTyVar -> CtFlavourRole -> RewriteM RewriteTvResult
rewrite_tyvar2 tv fr@(_, eq_rel)
= do { ieqs <- liftTcS $ getInertEqs
; case lookupDVarEnv ieqs tv of
- Just (EqualCtList (ct :| _)) -- If the first doesn't work,
- -- the subsequent ones won't either
- | CEqCan { cc_ev = ctev, cc_lhs = TyVarLHS tv
+ Just equal_ct_list
+ | Just ct <- find can_rewrite equal_ct_list
+ , CEqCan { cc_ev = ctev, cc_lhs = TyVarLHS tv
, cc_rhs = rhs_ty, cc_eq_rel = ct_eq_rel } <- ct
- , let ct_fr = (ctEvFlavour ctev, ct_eq_rel)
- , ct_fr `eqCanRewriteFR` fr -- This is THE key call of eqCanRewriteFR
- -> do { traceRewriteM "Following inert tyvar"
- (ppr tv <+>
- equals <+>
- ppr rhs_ty $$ ppr ctev)
- ; let rewriting_co1 = ctEvCoercion ctev
- rewriting_co = case (ct_eq_rel, eq_rel) of
- (ReprEq, _rel) -> assert (_rel == ReprEq )
- -- if this ASSERT fails, then
+ -> do { let wrw = isWantedCt ct
+ ; traceRewriteM "Following inert tyvar" $
+ vcat [ ppr tv <+> equals <+> ppr rhs_ty
+ , ppr ctev
+ , text "wanted_rewrite_wanted:" <+> ppr wrw ]
+ ; when wrw $ recordRewriter ctev
+
+ ; let rewriting_co1 = ctEvCoercion ctev
+ rewriting_co = case (ct_eq_rel, eq_rel) of
+ (ReprEq, _rel) -> assert (_rel == ReprEq)
+ -- if this assert fails, then
-- eqCanRewriteFR answered incorrectly
rewriting_co1
(NomEq, NomEq) -> rewriting_co1
(NomEq, ReprEq) -> mkSubCo rewriting_co1
- ; return $ RTRFollowed $ mkReduction rewriting_co rhs_ty }
- -- NB: ct is Derived then fmode must be also, hence
- -- we are not going to touch the returned coercion
- -- so ctEvCoercion is fine.
+ ; return $ RTRFollowed $ mkReduction rewriting_co rhs_ty }
_other -> return RTRNotFollowed }
+ where
+ can_rewrite :: Ct -> Bool
+ can_rewrite ct = ctFlavourRole ct `eqCanRewriteFR` fr
+ -- This is THE key call of eqCanRewriteFR
+
{-
Note [An alternative story for the inert substitution]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~