diff options
Diffstat (limited to 'compiler/GHC/Tc/Solver/Rewrite.hs')
| -rw-r--r-- | compiler/GHC/Tc/Solver/Rewrite.hs | 1028 |
1 files changed, 1028 insertions, 0 deletions
diff --git a/compiler/GHC/Tc/Solver/Rewrite.hs b/compiler/GHC/Tc/Solver/Rewrite.hs new file mode 100644 index 0000000000..7e4993ad8d --- /dev/null +++ b/compiler/GHC/Tc/Solver/Rewrite.hs @@ -0,0 +1,1028 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveFunctor #-} + +{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} + +module GHC.Tc.Solver.Rewrite( + rewrite, rewriteKind, rewriteArgsNom, + rewriteType + ) where + +#include "HsVersions.h" + +import GHC.Prelude + +import GHC.Core.TyCo.Ppr ( pprTyVar ) +import GHC.Tc.Types.Constraint +import GHC.Core.Predicate +import GHC.Tc.Utils.TcType +import GHC.Core.Type +import GHC.Tc.Types.Evidence +import GHC.Core.TyCon +import GHC.Core.TyCo.Rep -- performs delicate algorithm on types +import GHC.Core.Coercion +import GHC.Types.Var +import GHC.Types.Var.Set +import GHC.Types.Var.Env +import GHC.Driver.Session +import GHC.Utils.Outputable +import GHC.Utils.Panic +import GHC.Tc.Solver.Monad as TcS + +import GHC.Utils.Misc +import GHC.Data.Maybe +import Control.Monad +import GHC.Utils.Monad ( zipWith3M ) +import Data.List.NonEmpty ( NonEmpty(..) ) + +import Control.Arrow ( first ) + +{- +************************************************************************ +* * +* RewriteEnv & RewriteM +* The rewriting environment & monad +* * +************************************************************************ +-} + +data RewriteEnv + = FE { fe_loc :: !CtLoc -- See Note [Rewriter CtLoc] + , fe_flavour :: !CtFlavour + , fe_eq_rel :: !EqRel -- See Note [Rewriter EqRels] + } + +-- | The 'RewriteM' monad is a wrapper around 'TcS' with a 'RewriteEnv' +newtype RewriteM a + = RewriteM { runRewriteM :: RewriteEnv -> TcS a } + deriving (Functor) + +instance Monad RewriteM where + m >>= k = RewriteM $ \env -> + do { a <- runRewriteM m env + ; runRewriteM (k a) env } + +instance Applicative RewriteM where + pure x = RewriteM $ const (pure x) + (<*>) = ap + +instance HasDynFlags RewriteM where + getDynFlags = liftTcS getDynFlags + +liftTcS :: TcS a -> RewriteM a +liftTcS thing_inside + = RewriteM $ const thing_inside + +-- convenient wrapper when you have a CtEvidence describing +-- the rewriting operation +runRewriteCtEv :: CtEvidence -> RewriteM a -> TcS a +runRewriteCtEv ev + = runRewrite (ctEvLoc ev) (ctEvFlavour ev) (ctEvEqRel ev) + +-- Run thing_inside (which does the rewriting) +runRewrite :: CtLoc -> CtFlavour -> EqRel -> RewriteM a -> TcS a +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 } + +traceRewriteM :: String -> SDoc -> RewriteM () +traceRewriteM herald doc = liftTcS $ traceTcS herald doc +{-# INLINE traceRewriteM #-} -- see Note [INLINE conditional tracing utilities] + +getRewriteEnvField :: (RewriteEnv -> a) -> RewriteM a +getRewriteEnvField accessor + = RewriteM $ \env -> return (accessor env) + +getEqRel :: RewriteM EqRel +getEqRel = getRewriteEnvField fe_eq_rel + +getRole :: RewriteM Role +getRole = eqRelRole <$> getEqRel + +getFlavour :: RewriteM CtFlavour +getFlavour = getRewriteEnvField fe_flavour + +getFlavourRole :: RewriteM CtFlavourRole +getFlavourRole + = do { flavour <- getFlavour + ; eq_rel <- getEqRel + ; return (flavour, eq_rel) } + +getLoc :: RewriteM CtLoc +getLoc = getRewriteEnvField fe_loc + +checkStackDepth :: Type -> RewriteM () +checkStackDepth ty + = do { loc <- getLoc + ; liftTcS $ checkReductionDepth loc ty } + +-- | Change the 'EqRel' in a 'RewriteM'. +setEqRel :: EqRel -> RewriteM a -> RewriteM a +setEqRel new_eq_rel thing_inside + = RewriteM $ \env -> + if new_eq_rel == fe_eq_rel env + then runRewriteM thing_inside env + else runRewriteM thing_inside (env { fe_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 + = RewriteM $ \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) + = RewriteM $ \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) } + ; thing_inside env' } + +{- +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 +is ReprEq, and that we unwrap newtypes when rewriting w.r.t. +representational equality. + +Note [Rewriter CtLoc] +~~~~~~~~~~~~~~~~~~~~~~ +The rewriter does eager type-family reduction. +Type families might loop, and we +don't want GHC to do so. A natural solution is to have a bounded depth +to these processes. A central difficulty is that such a solution isn't +quite compositional. For example, say it takes F Int 10 steps to get to Bool. +How many steps does it take to get from F Int -> F Int to Bool -> Bool? +10? 20? What about getting from Const Char (F Int) to Char? 11? 1? Hard to +know and hard to track. So, we punt, essentially. We store a CtLoc in +the RewriteEnv and just update the environment when recurring. In the +TyConApp case, where there may be multiple type families to rewrite, +we just copy the current CtLoc into each branch. If any branch hits the +stack limit, then the whole thing fails. + +A consequence of this is that setting the stack limits appropriately +will be essentially impossible. So, the official recommendation if a +stack limit is hit is to disable the check entirely. Otherwise, there +will be baffling, unpredictable errors. + +Note [Phantoms in the rewriter] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Suppose we have + +data Proxy p = Proxy + +and we're rewriting (Proxy ty) w.r.t. ReprEq. Then, we know that `ty` +is really irrelevant -- it will be ignored when solving for representational +equality later on. So, we omit rewriting `ty` entirely. This may +violate the expectation of "xi"s for a bit, but the canonicaliser will +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. + +-} + +{- ********************************************************************* +* * +* Externally callable rewriting functions * +* * +************************************************************************ +-} + +-- | See Note [Rewriting]. +-- If (xi, co) <- rewrite mode ev ty, then co :: xi ~r ty +-- where r is the role in @ev@. +rewrite :: CtEvidence -> TcType + -> TcS (Xi, TcCoercion) +rewrite ev ty + = do { traceTcS "rewrite {" (ppr ty) + ; (ty', co) <- runRewriteCtEv ev (rewrite_one ty) + ; traceTcS "rewrite }" (ppr ty') + ; return (ty', co) } + +-- specialized to rewriting kinds: never Derived, always Nominal +-- See Note [No derived kind equalities] +-- See Note [Rewriting] +rewriteKind :: CtLoc -> CtFlavour -> TcType -> TcS (Xi, TcCoercionN) +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 + ; (ty', co) <- runRewrite loc flav' NomEq (rewrite_one ty) + ; traceTcS "rewriteKind }" (ppr ty' $$ ppr co) -- co is never a panic + ; return (ty', co) } + +-- See Note [Rewriting] +rewriteArgsNom :: CtEvidence -> TyCon -> [TcType] -> TcS ([Xi], [TcCoercion]) +-- 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 +-- ctEvFlavour ev = Nominal +-- and we want to rewrite all at nominal role +-- 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) +rewriteArgsNom ev tc tys + = do { traceTcS "rewrite_args {" (vcat (map ppr tys)) + ; (tys', cos, kind_co) + <- runRewriteCtEv ev (rewrite_args_tc tc Nothing tys) + ; MASSERT( isReflMCo kind_co ) + ; traceTcS "rewrite }" (vcat (map ppr tys')) + ; return (tys', cos) } + +-- | 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 +-- will never emit new constraints. Call this when the inert set contains +-- only givens. +rewriteType :: CtLoc -> TcType -> TcS TcType +rewriteType loc ty + = do { (xi, _) <- runRewrite loc Given NomEq $ + rewrite_one ty + -- use Given flavor so that it is rewritten + -- only w.r.t. Givens, never Wanteds/Deriveds + -- (Shouldn't matter, if only Givens are present + -- anyway) + ; return xi } + +{- ********************************************************************* +* * +* The main rewriting functions +* * +********************************************************************* -} + +{- Note [Rewriting] +~~~~~~~~~~~~~~~~~~~~ + rewrite ty ==> (xi, co) + where + xi has no reducible type functions + has no skolems that are mapped in the inert set + has no filled-in metavariables + co :: xi ~ ty + +Key invariants: + (F0) co :: xi ~ zonk(ty') where zonk(ty') ~ zonk(ty) + (F1) tcTypeKind(xi) succeeds and returns a fully zonked kind + (F2) tcTypeKind(xi) `eqType` zonk(tcTypeKind(ty)) + +Note that it is rewrite's job to try to reduce *every type function it sees*. + +Rewriting also: + * zonks, removing any metavariables, and + * applies the substitution embodied in the inert set + +Because rewriting zonks and the returned coercion ("co" above) is also +zonked, it's possible that (co :: xi ~ ty) isn't quite true. So, instead, +we can rely on this fact: + + (F0) co :: xi ~ zonk(ty'), where zonk(ty') ~ zonk(ty) + +Note that the left-hand type of co is *always* precisely xi. The right-hand +type may or may not be ty, however: if ty has unzonked filled-in metavariables, +then the right-hand type of co will be the zonk-equal to ty. +It is for this reason that we +occasionally have to explicitly zonk, when (co :: xi ~ ty) is important +even before we zonk the whole program. For example, see the RTRNotFollowed +case in rewriteTyVar. + +Why have these invariants on rewriting? Because we sometimes use tcTypeKind +during canonicalisation, and we want this kind to be zonked (e.g., see +GHC.Tc.Solver.Canonical.canEqCanLHS). + +Rewriting is always homogeneous. That is, the kind of the result of rewriting is +always the same as the kind of the input, modulo zonking. More formally: + + (F2) tcTypeKind(xi) `eqType` zonk(tcTypeKind(ty)) + +This invariant means that the kind of a rewritten type might not itself be rewritten. + +Note that we prefer to leave type synonyms unexpanded when possible, +so when the rewriter encounters one, it first asks whether its +transitive expansion contains any type function applications or is +forgetful -- that is, omits one or more type variables in its RHS. If so, +it expands the synonym and proceeds; if not, it simply returns the +unexpanded synonym. See also Note [Rewriting synonyms]. + +Note [rewrite_args performance] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In programs with lots of type-level evaluation, rewrite_args becomes +part of a tight loop. For example, see test perf/compiler/T9872a, which +calls rewrite_args a whopping 7,106,808 times. It is thus important +that rewrite_args be efficient. + +Performance testing showed that the current implementation is indeed +efficient. It's critically important that zipWithAndUnzipM be +specialized to TcS, and it's also quite helpful to actually `inline` +it. On test T9872a, here are the allocation stats (Dec 16, 2014): + + * Unspecialized, uninlined: 8,472,613,440 bytes allocated in the heap + * Specialized, uninlined: 6,639,253,488 bytes allocated in the heap + * Specialized, inlined: 6,281,539,792 bytes allocated in the heap + +To improve performance even further, rewrite_args_nom is split off +from rewrite_args, as nominal equality is the common case. This would +be natural to write using mapAndUnzipM, but even inlined, that function +is not as performant as a hand-written loop. + + * mapAndUnzipM, inlined: 7,463,047,432 bytes allocated in the heap + * hand-written recursion: 5,848,602,848 bytes allocated in the heap + +If you make any change here, pay close attention to the T9872{a,b,c} tests +and T5321Fun. + +If we need to make this yet more performant, a possible way forward is to +duplicate the rewriter code for the nominal case, and make that case +faster. This doesn't seem quite worth it, yet. + +Note [rewrite_exact_fam_app performance] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Once we've got a rewritten rhs, we extend the famapp-cache to record +the result. Doing so can save lots of work when the same redex shows up more +than once. Note that we record the link from the redex all the way to its +*final* value, not just the single step reduction. + +If we can reduce the family application right away (the first call +to try_to_reduce), we do *not* add to the cache. There are two possibilities +here: 1) we just read the result from the cache, or 2) we used one type +family instance. In either case, recording the result in the cache doesn't +save much effort the next time around. And adding to the cache here is +actually disastrous: it more than doubles the allocations for T9872a. So +we skip adding to the cache here. +-} + +{-# INLINE rewrite_args_tc #-} +rewrite_args_tc + :: TyCon -- T + -> Maybe [Role] -- Nothing: ambient role is Nominal; all args are Nominal + -- Otherwise: no assumptions; use roles provided + -> [Type] -- Arg types [t1,..,tn] + -> RewriteM ( [Xi] -- List of rewritten args [x1,..,xn] + -- 1-1 corresp with [t1,..,tn] + , [Coercion] -- List of arg coercions [co1,..,con] + -- 1-1 corresp with [t1,..,tn] + -- coi :: xi ~r ti + , MCoercionN) -- Result coercion, rco + -- rco : (T t1..tn) ~N (T (x1 |> co1) .. (xn |> con)) +rewrite_args_tc tc = rewrite_args all_bndrs any_named_bndrs inner_ki emptyVarSet + -- NB: TyCon kinds are always closed + where + (bndrs, named) + = ty_con_binders_ty_binders' (tyConBinders tc) + -- it's possible that the result kind has arrows (for, e.g., a type family) + -- so we must split it + (inner_bndrs, inner_ki, inner_named) = split_pi_tys' (tyConResKind tc) + !all_bndrs = bndrs `chkAppend` inner_bndrs + !any_named_bndrs = named || inner_named + -- NB: Those bangs there drop allocations in T9872{a,c,d} by 8%. + +{-# INLINE rewrite_args #-} +rewrite_args :: [TyCoBinder] -> Bool -- Binders, and True iff any of them are + -- named. + -> Kind -> TcTyCoVarSet -- function kind; kind's free vars + -> Maybe [Role] -> [Type] -- these are in 1-to-1 correspondence + -- Nothing: use all Nominal + -> RewriteM ([Xi], [Coercion], MCoercionN) +-- Coercions :: Xi ~ Type, at roles given +-- Third coercion :: tcTypeKind(fun xis) ~N tcTypeKind(fun tys) +-- That is, the third coercion relates the kind of some function (whose kind is +-- passed as the first parameter) instantiated at xis to the kind of that +-- function instantiated at the tys. This is useful in keeping rewriting +-- homoegeneous. The list of roles must be at least as long as the list of +-- types. +rewrite_args orig_binders + any_named_bndrs + orig_inner_ki + orig_fvs + orig_m_roles + orig_tys + = case (orig_m_roles, any_named_bndrs) of + (Nothing, False) -> rewrite_args_fast orig_tys + _ -> rewrite_args_slow orig_binders orig_inner_ki orig_fvs orig_roles orig_tys + where orig_roles = fromMaybe (repeat Nominal) orig_m_roles + +{-# INLINE rewrite_args_fast #-} +-- | fast path rewrite_args, in which none of the binders are named and +-- therefore we can avoid tracking a lifting context. +-- There are many bang patterns in here. It's been observed that they +-- greatly improve performance of an optimized build. +-- The T9872 test cases are good witnesses of this fact. +rewrite_args_fast :: [Type] + -> RewriteM ([Xi], [Coercion], MCoercionN) +rewrite_args_fast orig_tys + = fmap finish (iterate orig_tys) + where + + iterate :: [Type] + -> RewriteM ([Xi], [Coercion]) + iterate (ty:tys) = do + (xi, co) <- rewrite_one ty + (xis, cos) <- iterate tys + pure (xi : xis, co : cos) + iterate [] = pure ([], []) + + {-# INLINE finish #-} + finish :: ([Xi], [Coercion]) -> ([Xi], [Coercion], MCoercionN) + finish (xis, cos) = (xis, cos, MRefl) + +{-# INLINE rewrite_args_slow #-} +-- | Slow path, compared to rewrite_args_fast, because this one must track +-- a lifting context. +rewrite_args_slow :: [TyCoBinder] -> Kind -> TcTyCoVarSet + -> [Role] -> [Type] + -> RewriteM ([Xi], [Coercion], MCoercionN) +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 fl (map isNamedBinder binders ++ repeat True) + roles tys + ; return (simplifyArgsWorker binders inner_ki fvs roles rewritten_args) } + where + {-# INLINE fl #-} + fl :: Bool -- must we ensure to produce a real coercion here? + -- see comment at top of function + -> Role -> Type -> RewriteM (Xi, Coercion) + fl True r ty = noBogusCoercions $ fl1 r ty + fl False r ty = fl1 r ty + + {-# INLINE fl1 #-} + fl1 :: Role -> Type -> RewriteM (Xi, Coercion) + fl1 Nominal ty + = setEqRel NomEq $ + rewrite_one ty + + fl1 Representational ty + = setEqRel ReprEq $ + rewrite_one ty + + fl1 Phantom ty + -- See Note [Phantoms in the rewriter] + = do { ty <- liftTcS $ zonkTcType ty + ; return (ty, mkReflCo Phantom ty) } + +------------------ +rewrite_one :: TcType -> RewriteM (Xi, Coercion) +-- Rewrite a type to get rid of type function applications, returning +-- the new type-function-free type, and a collection of new equality +-- constraints. See Note [Rewriting] for more detail. +-- +-- Postcondition: Coercion :: Xi ~ TcType +-- The role on the result coercion matches the EqRel in the RewriteEnv + +rewrite_one ty + | Just ty' <- rewriterView ty -- See Note [Rewriting synonyms] + = rewrite_one ty' + +rewrite_one xi@(LitTy {}) + = do { role <- getRole + ; return (xi, mkReflCo role xi) } + +rewrite_one (TyVarTy tv) + = rewriteTyVar tv + +rewrite_one (AppTy ty1 ty2) + = rewrite_app_tys ty1 [ty2] + +rewrite_one (TyConApp tc tys) + -- If it's a type family application, try to reduce it + | isTypeFamilyTyCon tc + = rewrite_fam_app tc tys + + -- For * a normal data type application + -- * data family application + -- we just recursively rewrite the arguments. + | otherwise + = rewrite_ty_con_app tc tys + +rewrite_one ty@(FunTy { ft_mult = mult, ft_arg = ty1, ft_res = ty2 }) + = do { (xi1,co1) <- rewrite_one ty1 + ; (xi2,co2) <- rewrite_one ty2 + ; (xi3,co3) <- setEqRel NomEq $ rewrite_one mult + ; role <- getRole + ; return (ty { ft_mult = xi3, ft_arg = xi1, ft_res = xi2 } + , mkFunCo role co3 co1 co2) } + +rewrite_one ty@(ForAllTy {}) +-- TODO (RAE): This is inadequate, as it doesn't rewrite the kind of +-- the bound tyvar. Doing so will require carrying around a substitution +-- and the usual substTyVarBndr-like silliness. Argh. + +-- We allow for-alls when, but only when, no type function +-- applications inside the forall involve the bound type variables. + = do { let (bndrs, rho) = tcSplitForAllTyVarBinders ty + tvs = binderVars bndrs + ; (rho', co) <- rewrite_one rho + ; return (mkForAllTys bndrs rho', mkHomoForAllCos tvs co) } + +rewrite_one (CastTy ty g) + = do { (xi, co) <- rewrite_one ty + ; (g', _) <- rewrite_co g + ; role <- getRole + ; return (mkCastTy xi g', castCoercionKind1 co role xi ty g') } + -- It makes a /big/ difference to call castCoercionKind1 not + -- the more general castCoercionKind2. + -- See Note [castCoercionKind1] in GHC.Core.Coercion + +rewrite_one (CoercionTy co) = first mkCoercionTy <$> rewrite_co co + +-- | "Rewrite" a coercion. Really, just zonk it so we can uphold +-- (F1) of Note [Rewriting] +rewrite_co :: Coercion -> RewriteM (Coercion, Coercion) +rewrite_co co + = do { co <- liftTcS $ zonkCo co + ; env_role <- getRole + ; let co' = mkTcReflCo env_role (mkCoercionTy co) + ; return (co, co') } + +-- rewrite (nested) AppTys +rewrite_app_tys :: Type -> [Type] -> RewriteM (Xi, Coercion) +-- commoning up nested applications allows us to look up the function's kind +-- only once. Without commoning up like this, we would spend a quadratic amount +-- of time looking up functions' types +rewrite_app_tys (AppTy ty1 ty2) tys = rewrite_app_tys ty1 (ty2:tys) +rewrite_app_tys fun_ty arg_tys + = do { (fun_xi, fun_co) <- rewrite_one fun_ty + ; rewrite_app_ty_args fun_xi fun_co arg_tys } + +-- Given a rewritten function (with the coercion produced by rewriting) and +-- a bunch of unrewritten arguments, rewrite the arguments and apply. +-- The coercion argument's role matches the role stored in the RewriteM monad. +-- +-- The bang patterns used here were observed to improve performance. If you +-- wish to remove them, be sure to check for regeressions in allocations. +rewrite_app_ty_args :: Xi -> Coercion -> [Type] -> RewriteM (Xi, Coercion) +rewrite_app_ty_args fun_xi fun_co [] + -- this will be a common case when called from rewrite_fam_app, so shortcut + = return (fun_xi, fun_co) +rewrite_app_ty_args fun_xi fun_co arg_tys + = do { (xi, co, kind_co) <- case tcSplitTyConApp_maybe fun_xi of + Just (tc, xis) -> + do { let tc_roles = tyConRolesRepresentational tc + arg_roles = dropList xis tc_roles + ; (arg_xis, arg_cos, kind_co) + <- rewrite_vector (tcTypeKind fun_xi) arg_roles arg_tys + + -- Here, we have fun_co :: T xi1 xi2 ~ ty + -- and we need to apply fun_co to the arg_cos. The problem is + -- that using mkAppCo is wrong because that function expects + -- its second coercion to be Nominal, and the arg_cos might + -- not be. The solution is to use transitivity: + -- T <xi1> <xi2> arg_cos ;; fun_co <arg_tys> + ; eq_rel <- getEqRel + ; let app_xi = mkTyConApp tc (xis ++ arg_xis) + app_co = case eq_rel of + NomEq -> mkAppCos fun_co arg_cos + ReprEq -> mkTcTyConAppCo Representational tc + (zipWith mkReflCo tc_roles xis ++ arg_cos) + `mkTcTransCo` + mkAppCos fun_co (map mkNomReflCo arg_tys) + ; return (app_xi, app_co, kind_co) } + Nothing -> + do { (arg_xis, arg_cos, kind_co) + <- rewrite_vector (tcTypeKind fun_xi) (repeat Nominal) arg_tys + ; let arg_xi = mkAppTys fun_xi arg_xis + arg_co = mkAppCos fun_co arg_cos + ; return (arg_xi, arg_co, kind_co) } + + ; role <- getRole + ; return (homogenise_result xi co role kind_co) } + +rewrite_ty_con_app :: TyCon -> [TcType] -> RewriteM (Xi, Coercion) +rewrite_ty_con_app tc tys + = do { role <- getRole + ; let m_roles | Nominal <- role = Nothing + | otherwise = Just $ tyConRolesX role tc + ; (xis, cos, kind_co) <- rewrite_args_tc tc m_roles tys + ; let tyconapp_xi = mkTyConApp tc xis + tyconapp_co = mkTyConAppCo role tc cos + ; return (homogenise_result tyconapp_xi tyconapp_co role kind_co) } + +-- Make the result of rewriting homogeneous (Note [Rewriting] (F2)) +homogenise_result :: Xi -- a rewritten type + -> Coercion -- :: xi ~r original ty + -> Role -- r + -> MCoercionN -- kind_co :: tcTypeKind(xi) ~N tcTypeKind(ty) + -> (Xi, Coercion) -- (xi |> kind_co, (xi |> kind_co) + -- ~r original ty) +homogenise_result xi co _ MRefl = (xi, co) +homogenise_result xi co r mco@(MCo kind_co) + = (xi `mkCastTy` kind_co, (mkSymCo $ GRefl r xi mco) `mkTransCo` co) +{-# INLINE homogenise_result #-} + +-- Rewrite a vector (list of arguments). +rewrite_vector :: Kind -- of the function being applied to these arguments + -> [Role] -- If we're rewrite w.r.t. ReprEq, what roles do the + -- args have? + -> [Type] -- the args to rewrite + -> RewriteM ([Xi], [Coercion], MCoercionN) +rewrite_vector ki roles tys + = do { eq_rel <- getEqRel + ; case eq_rel of + NomEq -> rewrite_args bndrs + any_named_bndrs + inner_ki + fvs + Nothing + tys + ReprEq -> rewrite_args bndrs + any_named_bndrs + inner_ki + fvs + (Just roles) + tys + } + where + (bndrs, inner_ki, any_named_bndrs) = split_pi_tys' ki -- "RAE" fix + fvs = tyCoVarsOfType ki +{-# INLINE rewrite_vector #-} + +{- +Note [Rewriting synonyms] +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Not expanding synonyms aggressively improves error messages, and +keeps types smaller. But we need to take care. + +Suppose + type Syn a = Int + type instance F Bool = Syn (F Bool) + [G] F Bool ~ Syn (F Bool) + +If we don't expand the synonym, we'll get a spurious occurs-check +failure. This is normally what occCheckExpand takes care of, but +the LHS is a type family application, and occCheckExpand (already +complex enough as it is) does not know how to expand to avoid +a type family application. + +In addition, expanding the forgetful synonym like this +will generally yield a *smaller* type. To wit, if we spot +S ( ... F tys ... ), where S is forgetful, we don't want to bother +doing hard work simplifying (F tys). We thus expand forgetful +synonyms, but not others. + +isForgetfulSynTyCon returns True more often than it needs to, so +we err on the side of more expansion. + +We also, of course, must expand type synonyms that mention type families, +so those families can get reduced. + +************************************************************************ +* * + Rewriting a type-family application +* * +************************************************************************ + +Note [How to normalise a family application] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Given an exactly saturated family application, how should we normalise it? +This Note spells out the algorithm and its reasoning. + +STEP 1. Try the famapp-cache. If we get a cache hit, jump to FINISH. + +STEP 2. Try top-level instances. Note that we haven't simplified the arguments + yet. Example: + type instance F (Maybe a) = Int + target: F (Maybe (G Bool)) + Instead of first trying to simplify (G Bool), we use the instance first. This + avoids the work of simplifying G Bool. + + If an instance is found, jump to FINISH. + +STEP 3. Rewrite all arguments. This might expose more information so that we + can use a top-level instance. + + Continue to the next step. + +STEP 4. Try the inerts. Note that we try the inerts *after* rewriting the + arguments, because the inerts will have rewritten LHSs. + + If an inert is found, jump to FINISH. + +STEP 5. Try the famapp-cache again. Now that we've revealed more information + in the arguments, the cache might be helpful. + + If we get a cache hit, jump to FINISH. + +STEP 6. Try top-level instances, which might trigger now that we know more + about the argumnents. + + If an instance is found, jump to FINISH. + +STEP 7. No progress to be made. Return what we have. (Do not do FINISH.) + +FINISH 1. We've made a reduction, but the new type may still have more + work to do. So rewrite the new type. + +FINISH 2. Add the result to the famapp-cache, connecting the type we started + with to the one we ended with. + +Because STEP 1/2 and STEP 5/6 happen the same way, they are abstracted into +try_to_reduce. + +FINISH is naturally implemented in `finish`. But, Note [rewrite_exact_fam_app performance] +tells us that we should not add to the famapp-cache after STEP 1/2. So `finish` +is inlined in that case, and only FINISH 1 is performed. + +-} + +rewrite_fam_app :: TyCon -> [TcType] -> RewriteM (Xi, Coercion) + -- rewrite_fam_app can be over-saturated + -- rewrite_exact_fam_app lifts out the application to top level + -- Postcondition: Coercion :: Xi ~ F tys +rewrite_fam_app tc tys -- Can be over-saturated + = ASSERT2( tys `lengthAtLeast` tyConArity tc + , ppr tc $$ ppr (tyConArity tc) $$ ppr tys) + + -- Type functions are saturated + -- The type function might be *over* saturated + -- in which case the remaining arguments should + -- be dealt with by AppTys + do { let (tys1, tys_rest) = splitAt (tyConArity tc) tys + ; (xi1, co1) <- rewrite_exact_fam_app tc tys1 + -- co1 :: xi1 ~ F tys1 + + ; rewrite_app_ty_args xi1 co1 tys_rest } + +-- the [TcType] exactly saturate the TyCon +-- See Note [How to normalise a family application] +rewrite_exact_fam_app :: TyCon -> [TcType] -> RewriteM (Xi, Coercion) +rewrite_exact_fam_app tc tys + = do { checkStackDepth (mkTyConApp tc tys) + + -- STEP 1/2. Try to reduce without reducing arguments first. + ; result1 <- try_to_reduce tc tys + ; case result1 of + -- Don't use the cache; + -- See Note [rewrite_exact_fam_app performance] + { Just (co, xi) -> finish False (xi, co) + ; Nothing -> + + -- That didn't work. So reduce the arguments, in STEP 3. + do { eq_rel <- getEqRel + -- checking eq_rel == NomEq saves ~0.5% in T9872a + ; (xis, cos, kind_co) <- if eq_rel == NomEq + then rewrite_args_tc tc Nothing tys + else setEqRel NomEq $ + rewrite_args_tc tc Nothing tys + -- kind_co :: tcTypeKind(F xis) ~N tcTypeKind(F tys) + + ; let role = eqRelRole eq_rel + args_co = mkTyConAppCo role tc cos + -- args_co :: F xis ~r F tys + + homogenise :: TcType -> TcCoercion -> (TcType, TcCoercion) + -- in (xi', co') = homogenise xi co + -- assume co :: xi ~r F xis, co is homogeneous + -- then xi' :: tcTypeKind(F tys) + -- and co' :: xi' ~r F tys, which is homogeneous + homogenise xi co = homogenise_result xi (co `mkTcTransCo` args_co) role kind_co + + -- STEP 4: try the inerts + ; result2 <- liftTcS $ lookupFamAppInert tc xis + ; flavour <- getFlavour + ; case result2 of + { Just (co, xi, fr@(_, inert_eq_rel)) + -- co :: F xis ~ir xi + + | fr `eqCanRewriteFR` (flavour, eq_rel) -> + do { traceRewriteM "rewrite family application with inert" + (ppr tc <+> ppr xis $$ ppr xi) + ; finish True (homogenise xi downgraded_co) } + -- 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 + where + inert_role = eqRelRole inert_eq_rel + role = eqRelRole eq_rel + downgraded_co = tcDowngradeRole role inert_role (mkTcSymCo co) + -- downgraded_co :: xi ~r F xis + + ; _ -> + + -- inert didn't work. Try to reduce again, in STEP 5/6. + do { result3 <- try_to_reduce tc xis + ; case result3 of + Just (co, xi) -> finish True (homogenise xi co) + Nothing -> -- we have made no progress at all: STEP 7. + return (homogenise reduced (mkTcReflCo role reduced)) + where + reduced = mkTyConApp tc xis }}}}} + where + -- 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? + -> (Xi, Coercion) -> RewriteM (Xi, Coercion) + finish use_cache (xi, co) + = do { -- rewrite the result: FINISH 1 + (fully, fully_co) <- bumpDepth $ rewrite_one xi + ; let final_co = fully_co `mkTcTransCo` co + ; eq_rel <- getEqRel + ; flavour <- getFlavour + + -- extend the cache: FINISH 2 + ; when (use_cache && eq_rel == NomEq && flavour /= Derived) $ + -- the cache only wants Nominal eqs + -- and Wanteds can rewrite Deriveds; the cache + -- has only Givens + liftTcS $ extendFamAppCache tc tys (final_co, fully) + ; return (fully, final_co) } + {-# INLINE finish #-} + +-- Returned coercion is output ~r input, where r is the role in the RewriteM monad +-- See Note [How to normalise a family application] +try_to_reduce :: TyCon -> [TcType] -> RewriteM (Maybe (TcCoercion, TcType)) +try_to_reduce tc tys + = do { result <- liftTcS $ firstJustsM [ lookupFamAppCache tc tys -- STEP 5 + , matchFam tc tys ] -- STEP 6 + ; downgrade result } + where + -- The result above is always Nominal. We might want a Representational + -- coercion; this downgrades (and prints, out of convenience). + downgrade :: Maybe (TcCoercionN, TcType) -> RewriteM (Maybe (TcCoercion, TcType)) + downgrade Nothing = return Nothing + downgrade result@(Just (co, xi)) + = do { traceRewriteM "Eager T.F. reduction success" $ + vcat [ ppr tc, ppr tys, ppr xi + , ppr co <+> dcolon <+> ppr (coercionKind co) + ] + ; eq_rel <- getEqRel + -- manually doing it this way avoids allocation in the vastly + -- common NomEq case + ; case eq_rel of + NomEq -> return result + ReprEq -> return (Just (mkSubCo co, xi)) } + +{- +************************************************************************ +* * + Rewriting a type variable +* * +********************************************************************* -} + +-- | The result of rewriting a tyvar "one step". +data RewriteTvResult + = RTRNotFollowed + -- ^ The inert set doesn't make the tyvar equal to anything else + + | RTRFollowed TcType Coercion + -- ^ The tyvar rewrites to a not-necessarily rewritten other type. + -- co :: new type ~r old type, where the role is determined by + -- the RewriteEnv + +rewriteTyVar :: TyVar -> RewriteM (Xi, Coercion) +rewriteTyVar tv + = do { mb_yes <- rewrite_tyvar1 tv + ; case mb_yes of + RTRFollowed ty1 co1 -- Recur + -> do { (ty2, co2) <- rewrite_one ty1 + -- ; traceRewriteM "rewriteTyVar2" (ppr tv $$ ppr ty2) + ; return (ty2, co2 `mkTransCo` co1) } + + RTRNotFollowed -- Done, but make sure the kind is zonked + -- Note [Rewriting] invariant (F0) and (F1) + -> do { tv' <- liftTcS $ updateTyVarKindM zonkTcType tv + ; role <- getRole + ; let ty' = mkTyVarTy tv' + ; return (ty', mkTcReflCo role ty') } } + +rewrite_tyvar1 :: TcTyVar -> RewriteM RewriteTvResult +-- "Rewriting" a type variable means to apply the substitution to it +-- Specifically, look up the tyvar in +-- * the internal MetaTyVar box +-- * the inerts +-- See also the documentation for RewriteTvResult + +rewrite_tyvar1 tv + = do { mb_ty <- liftTcS $ isFilledMetaTyVar_maybe tv + ; case mb_ty of + Just ty -> do { traceRewriteM "Following filled tyvar" + (ppr tv <+> equals <+> ppr ty) + ; role <- getRole + ; return (RTRFollowed ty (mkReflCo role ty)) } ; + Nothing -> do { traceRewriteM "Unfilled tyvar" (pprTyVar tv) + ; fr <- getFlavourRole + ; rewrite_tyvar2 tv fr } } + +rewrite_tyvar2 :: TcTyVar -> CtFlavourRole -> RewriteM RewriteTvResult +-- The tyvar is not a filled-in meta-tyvar +-- Try in the inert equalities +-- See Definition [Applying a generalised substitution] in GHC.Tc.Solver.Monad +-- See Note [Stability of rewriting] in GHC.Tc.Solver.Monad + +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 + , 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 rewrite_co1 = mkSymCo (ctEvCoercion ctev) + rewrite_co = case (ct_eq_rel, eq_rel) of + (ReprEq, _rel) -> ASSERT( _rel == ReprEq ) + -- if this ASSERT fails, then + -- eqCanRewriteFR answered incorrectly + rewrite_co1 + (NomEq, NomEq) -> rewrite_co1 + (NomEq, ReprEq) -> mkSubCo rewrite_co1 + + ; return (RTRFollowed rhs_ty rewrite_co) } + -- NB: ct is Derived then fmode must be also, hence + -- we are not going to touch the returned coercion + -- so ctEvCoercion is fine. + + _other -> return RTRNotFollowed } + +{- +Note [An alternative story for the inert substitution] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(This entire note is just background, left here in case we ever want + to return the previous state of affairs) + +We used (GHC 7.8) to have this story for the inert substitution inert_eqs + + * 'a' is not in fvs(ty) + * They are *inert* in the weaker sense that there is no infinite chain of + (i1 `eqCanRewrite` i2), (i2 `eqCanRewrite` i3), etc + +This means that rewriting must be recursive, but it does allow + [G] a ~ [b] + [G] b ~ Maybe c + +This avoids "saturating" the Givens, which can save a modest amount of work. +It is easy to implement, in GHC.Tc.Solver.Interact.kick_out, by only kicking out an inert +only if (a) the work item can rewrite the inert AND + (b) the inert cannot rewrite the work item + +This is significantly harder to think about. It can save a LOT of work +in occurs-check cases, but we don't care about them much. #5837 +is an example, but it causes trouble only with the old (pre-Fall 2020) +rewriting story. It is unclear if there is any gain w.r.t. to +the new story. + +-} + +-------------------------------------- +-- Utilities + +-- | Like 'splitPiTys'' but comes with a 'Bool' which is 'True' iff there is at +-- least one named binder. +split_pi_tys' :: Type -> ([TyCoBinder], Type, Bool) +split_pi_tys' ty = split ty ty + where + -- put common cases first + split _ (ForAllTy b res) = let (bs, ty, _) = split res res + in (Named b : bs, ty, True) + split _ (FunTy { ft_af = af, ft_mult = w, ft_arg = arg, ft_res = res }) + = let (bs, ty, named) = split res res + in (Anon af (mkScaled w arg) : bs, ty, named) + + split orig_ty ty | Just ty' <- coreView ty = split orig_ty ty' + split orig_ty _ = ([], orig_ty, False) +{-# INLINE split_pi_tys' #-} + +-- | Like 'tyConBindersTyCoBinders' but you also get a 'Bool' which is true iff +-- there is at least one named binder. +ty_con_binders_ty_binders' :: [TyConBinder] -> ([TyCoBinder], Bool) +ty_con_binders_ty_binders' = foldr go ([], False) + where + go (Bndr tv (NamedTCB vis)) (bndrs, _) + = (Named (Bndr tv vis) : bndrs, True) + go (Bndr tv (AnonTCB af)) (bndrs, n) + = (Anon af (tymult (tyVarKind tv)) : bndrs, n) + {-# INLINE go #-} +{-# INLINE ty_con_binders_ty_binders' #-} |
