diff options
Diffstat (limited to 'compiler/GHC/Core/Coercion/Opt.hs')
-rw-r--r-- | compiler/GHC/Core/Coercion/Opt.hs | 802 |
1 files changed, 583 insertions, 219 deletions
diff --git a/compiler/GHC/Core/Coercion/Opt.hs b/compiler/GHC/Core/Coercion/Opt.hs index 98506c444e..8df3dc31e5 100644 --- a/compiler/GHC/Core/Coercion/Opt.hs +++ b/compiler/GHC/Core/Coercion/Opt.hs @@ -1,10 +1,15 @@ -- (c) The University of Glasgow 2006 {-# LANGUAGE CPP #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE StandaloneKindSignatures #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} module GHC.Core.Coercion.Opt ( optCoercion , OptCoercionOpts (..) + , OptDCoMethod (..) ) where @@ -21,6 +26,7 @@ import GHC.Core.TyCon import GHC.Core.Coercion.Axiom import GHC.Core.Unify +import GHC.Types.Var import GHC.Types.Var.Set import GHC.Types.Var.Env import GHC.Types.Unique.Set @@ -35,6 +41,7 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import Control.Monad ( zipWithM ) +import qualified Data.Kind ( Type ) {- %************************************************************************ @@ -123,31 +130,40 @@ So we substitute the coercion variable c for the coercion -- | Coercion optimisation options newtype OptCoercionOpts = OptCoercionOpts - { optCoercionEnabled :: Bool -- ^ Enable coercion optimisation (reduce its size) + { optCoercionOpts :: Maybe OptDCoMethod + -- ^ @Nothing@: no coercion optimisation. + -- ^ @Just opt@: do full coercion optimisation, with @opt@ specifying + -- how to deal with directed coercions. } +data OptDCoMethod + = HydrateDCos + -- ^ Turn directed coercions back into fully-fledged coercions in the + -- coercion optimiser, so that they can be fully optimised. + | OptDCos + -- ^ Optimise directed coercions with the (currently limited) + -- forms of optimisation avaiable for directed coercions. + { skipDCoOpt :: !Bool + -- ^ Whether to skip optimisation of directed coercions entirely + -- when possible. + } + +data OptCoParams = + OptCoParams { optDCoMethod :: !OptDCoMethod } + optCoercion :: OptCoercionOpts -> Subst -> Coercion -> NormalCo -- ^ optCoercion applies a substitution to a coercion, -- *and* optimises it to reduce its size -optCoercion opts env co - | optCoercionEnabled opts - = optCoercion' env co -{- - = pprTrace "optCoercion {" (text "Co:" <+> ppr co) $ - let result = optCoercion' env co in - pprTrace "optCoercion }" (vcat [ text "Co:" <+> ppr co - , text "Optco:" <+> ppr result ]) $ - result --} - - | otherwise - = substCo env co - - -optCoercion' :: Subst -> Coercion -> NormalCo -optCoercion' env co +optCoercion (OptCoercionOpts opts) env co + | Just dco_method <- opts = optCoercion' + (OptCoParams { optDCoMethod = dco_method }) env + $ co + | otherwise = substCo env $ co + +optCoercion' :: OptCoParams -> Subst -> Coercion -> NormalCo +optCoercion' opts env co | debugIsOn - = let out_co = opt_co1 lc False co + = let out_co = opt_co1 opts lc False co (Pair in_ty1 in_ty2, in_role) = coercionKindRole co (Pair out_ty1 out_ty2, out_role) = coercionKindRole out_co in @@ -167,7 +183,7 @@ optCoercion' env co , text "subst:" <+> ppr env ])) out_co - | otherwise = opt_co1 lc False co + | otherwise = opt_co1 opts lc False co where lc = mkSubstLiftingContext env ppr_one cv = ppr cv <+> dcolon <+> ppr (coVarKind cv) @@ -179,7 +195,10 @@ type NormalCo = Coercion -- * For trans coercions (co1 `trans` co2) -- co1 is not a trans, and neither co1 nor co2 is identity +type NormalDCo = DCoercion + type NormalNonIdCo = NormalCo -- Extra invariant: not the identity +type NormalNonIdDCo = NormalDCo -- Extra invariant: not the identity -- | Do we apply a @sym@ to the result? type SymFlag = Bool @@ -189,65 +208,71 @@ type ReprFlag = Bool -- | Optimize a coercion, making no assumptions. All coercions in -- the lifting context are already optimized (and sym'd if nec'y) -opt_co1 :: LiftingContext +opt_co1 :: OptCoParams + -> LiftingContext -> SymFlag -> Coercion -> NormalCo -opt_co1 env sym co = opt_co2 env sym (coercionRole co) co +opt_co1 opts env sym co = opt_co2 opts env sym (coercionRole co) co -- See Note [Optimising coercion optimisation] -- | Optimize a coercion, knowing the coercion's role. No other assumptions. -opt_co2 :: LiftingContext +opt_co2 :: OptCoParams + -> LiftingContext -> SymFlag -> Role -- ^ The role of the input coercion -> Coercion -> NormalCo -opt_co2 env sym Phantom co = opt_phantom env sym co -opt_co2 env sym r co = opt_co3 env sym Nothing r co +opt_co2 opts env sym Phantom co = opt_phantom opts env sym co +opt_co2 opts env sym r co = opt_co3 opts env sym Nothing r co -- See Note [Optimising coercion optimisation] -- | Optimize a coercion, knowing the coercion's non-Phantom role. -opt_co3 :: LiftingContext -> SymFlag -> Maybe Role -> Role -> Coercion -> NormalCo -opt_co3 env sym (Just Phantom) _ co = opt_phantom env sym co -opt_co3 env sym (Just Representational) r co = opt_co4_wrap env sym True r co +opt_co3 :: OptCoParams -> LiftingContext -> SymFlag -> Maybe Role -> Role -> Coercion -> NormalCo +opt_co3 opts env sym (Just Phantom) _ co = opt_phantom opts env sym co +opt_co3 opts env sym (Just Representational) r co = opt_co4 opts env sym True r co -- if mrole is Just Nominal, that can't be a downgrade, so we can ignore -opt_co3 env sym _ r co = opt_co4_wrap env sym False r co +opt_co3 opts env sym _ r co = opt_co4 opts env sym False r co + +-- | Utility function for debugging coercion optimisation: uncomment +-- the logging functions in the body of this function, and the coercion +-- optimiser will produce a log of what it is doing. +wrap :: (Outputable in_co, Outputable out_co) => String -> Optimiser in_co out_co -> Optimiser in_co out_co +wrap _str opt_thing opts env sym rep r co + = {- pprTrace (_str ++ " wrap {") + ( vcat [ text "Sym:" <+> ppr sym + , text "Rep:" <+> ppr rep + , text "Role:" <+> ppr r + , text "Co:" <+> ppr co + , text "LC:" <+> ppr env + , text "Subst:" <+> ppr (lcTCvSubst env)]) $ + --assert (r == coercionRole co) + pprTrace (_str ++ " wrap }") (ppr co $$ text "---" $$ ppr result) $ -} + result + where result = opt_thing opts env sym rep r co -- See Note [Optimising coercion optimisation] -- | Optimize a non-phantom coercion. -opt_co4, opt_co4_wrap :: LiftingContext -> SymFlag -> ReprFlag - -> Role -> Coercion -> NormalCo +opt_co4_wrap :: String -> OptCoParams -> LiftingContext -> SymFlag -> ReprFlag -> Role -> Coercion -> NormalCo -- Precondition: In every call (opt_co4 lc sym rep role co) -- we should have role = coercionRole co -opt_co4_wrap = opt_co4 - -{- -opt_co4_wrap env sym rep r co - = pprTrace "opt_co4_wrap {" - ( vcat [ text "Sym:" <+> ppr sym - , text "Rep:" <+> ppr rep - , text "Role:" <+> ppr r - , text "Co:" <+> ppr co ]) $ - assert (r == coercionRole co ) $ - let result = opt_co4 env sym rep r co in - pprTrace "opt_co4_wrap }" (ppr co $$ text "---" $$ ppr result) $ - result --} +opt_co4_wrap str opts env sym rep r co = wrap ("opt_co4 " ++ str) opt_co4 opts env sym rep r co -opt_co4 env _ rep r (Refl ty) +opt_co4 :: OptCoParams -> LiftingContext -> SymFlag -> ReprFlag -> Role -> Coercion -> NormalCo +opt_co4 _ env _ rep r (Refl ty) = assertPpr (r == Nominal) (text "Expected role:" <+> ppr r $$ text "Found role:" <+> ppr Nominal $$ text "Type:" <+> ppr ty) $ liftCoSubst (chooseRole rep r) env ty -opt_co4 env _ rep r (GRefl _r ty MRefl) +opt_co4 _ env _ rep r (GRefl _r ty MRefl) = assertPpr (r == _r) (text "Expected role:" <+> ppr r $$ text "Found role:" <+> ppr _r $$ text "Type:" <+> ppr ty) $ liftCoSubst (chooseRole rep r) env ty -opt_co4 env sym rep r (GRefl _r ty (MCo co)) +opt_co4 opts env sym rep r (GRefl _r ty (MCo co)) = assertPpr (r == _r) (text "Expected role:" <+> ppr r $$ text "Found role:" <+> ppr _r $$ @@ -258,58 +283,58 @@ opt_co4 env sym rep r (GRefl _r ty (MCo co)) where r' = chooseRole rep r ty' = substTy (lcSubstLeft env) ty - co' = opt_co4 env False False Nominal co + co' = opt_co4 opts env False False Nominal co -opt_co4 env sym rep r (SymCo co) = opt_co4_wrap env (not sym) rep r co +opt_co4 opts env sym rep r (SymCo co) = opt_co4_wrap "SymCo" opts env (not sym) rep r co -- surprisingly, we don't have to do anything to the env here. This is -- because any "lifting" substitutions in the env are tied to ForAllCos, -- which treat their left and right sides differently. We don't want to -- exchange them. -opt_co4 env sym rep r g@(TyConAppCo _r tc cos) +opt_co4 opts env sym rep r g@(TyConAppCo _r tc cos) = assert (r == _r) $ case (rep, r) of (True, Nominal) -> mkTyConAppCo Representational tc - (zipWith3 (opt_co3 env sym) + (zipWith3 (opt_co3 opts env sym) (map Just (tyConRoleListRepresentational tc)) (repeat Nominal) cos) (False, Nominal) -> - mkTyConAppCo Nominal tc (map (opt_co4_wrap env sym False Nominal) cos) + mkTyConAppCo Nominal tc (map (opt_co4_wrap "TyConAppCo (False, Nominal)" opts env sym False Nominal) cos) (_, Representational) -> -- must use opt_co2 here, because some roles may be P -- See Note [Optimising coercion optimisation] - mkTyConAppCo r tc (zipWith (opt_co2 env sym) + mkTyConAppCo r tc (zipWith (opt_co2 opts env sym) (tyConRoleListRepresentational tc) -- the current roles cos) (_, Phantom) -> pprPanic "opt_co4 sees a phantom!" (ppr g) -opt_co4 env sym rep r (AppCo co1 co2) - = mkAppCo (opt_co4_wrap env sym rep r co1) - (opt_co4_wrap env sym False Nominal co2) +opt_co4 opts env sym rep r (AppCo co1 co2) + = mkAppCo (opt_co4_wrap "AppCo co1" opts env sym rep r co1) + (opt_co4_wrap "AppCo co2" opts env sym False Nominal co2) -opt_co4 env sym rep r (ForAllCo tv k_co co) - = case optForAllCoBndr env sym tv k_co of +opt_co4 opts env sym rep r (ForAllCo tv k_co co) + = case optForAllCoBndr opts env sym tv k_co of (env', tv', k_co') -> mkForAllCo tv' k_co' $ - opt_co4_wrap env' sym rep r co + opt_co4_wrap "ForAllCo" opts env' sym rep r co -- Use the "mk" functions to check for nested Refls -opt_co4 env sym rep r (FunCo _r afl afr cow co1 co2) +opt_co4 opts env sym rep r (FunCo _r afl afr cow co1 co2) = assert (r == _r) $ mkFunCo2 r' afl' afr' cow' co1' co2' where - co1' = opt_co4_wrap env sym rep r co1 - co2' = opt_co4_wrap env sym rep r co2 - cow' = opt_co1 env sym cow + co1' = opt_co4_wrap "FunCo co1" opts env sym rep r co1 + co2' = opt_co4_wrap "FunCo co2" opts env sym rep r co2 + cow' = opt_co1 opts env sym cow !r' | rep = Representational | otherwise = r !(afl', afr') | sym = (afr,afl) | otherwise = (afl,afr) -opt_co4 env sym rep r (CoVarCo cv) +opt_co4 opts env sym rep r (CoVarCo cv) | Just co <- lookupCoVar (lcSubst env) cv - = opt_co4_wrap (zapLiftingContext env) sym rep r co + = opt_co4_wrap "CoVarCo" opts (zapLiftingContext env) sym rep r co | ty1 `eqType` ty2 -- See Note [Optimise CoVarCo to Refl] = mkReflCo (chooseRole rep r) ty1 @@ -330,10 +355,10 @@ opt_co4 env sym rep r (CoVarCo cv) cv -- cv1 might have a substituted kind! -opt_co4 _ _ _ _ (HoleCo h) +opt_co4 _ _ _ _ _ (HoleCo h) = pprPanic "opt_univ fell into a hole" (ppr h) -opt_co4 env sym rep r (AxiomInstCo con ind cos) +opt_co4 opts env sym rep r (AxiomInstCo con ind cos) -- Do *not* push sym inside top-level axioms -- e.g. if g is a top-level axiom -- g a : f a ~ a @@ -343,43 +368,64 @@ opt_co4 env sym rep r (AxiomInstCo con ind cos) wrapSym sym $ -- some sub-cos might be P: use opt_co2 -- See Note [Optimising coercion optimisation] - AxiomInstCo con ind (zipWith (opt_co2 env False) + AxiomInstCo con ind (zipWith (opt_co2 opts env False) (coAxBranchRoles (coAxiomNthBranch con ind)) cos) -- Note that the_co does *not* have sym pushed into it -opt_co4 env sym rep r (UnivCo prov _r t1 t2) - = assert (r == _r ) - opt_univ env sym prov (chooseRole rep r) t1 t2 +opt_co4 opts env@(LC _ _lift_co_env) sym rep r (HydrateDCo _r lhs_ty dco rhs_ty) + = case optDCoMethod opts of + HydrateDCos -> + opt_co4 opts env sym rep r (hydrateOneLayerDCo r lhs_ty dco) + OptDCos { skipDCoOpt = do_skip } + | do_skip && isEmptyVarEnv _lift_co_env + -> let res = substCo (lcSubst env) (HydrateDCo r lhs_ty dco rhs_ty) + in assert (r == _r) $ + wrapSym sym $ + wrapRole rep r $ + res + | otherwise + -> assert (r == _r) $ + wrapSym sym $ + (\ (lhs', dco') -> mkHydrateDCo r' lhs' dco' rhs') $ + opt_dco4_wrap "HydrateDCo" opts env rep r lhs_ty dco + where + rhs' = substTyUnchecked (lcSubstRight env) rhs_ty + r' = chooseRole rep r + +opt_co4 opts env sym rep r (UnivCo prov _r t1 t2) + = assert (r == _r) $ + opt_univ Co opts env sym prov (chooseRole rep r) t1 t2 -opt_co4 env sym rep r (TransCo co1 co2) +opt_co4 opts env sym rep r (TransCo co1 co2) -- sym (g `o` h) = sym h `o` sym g - | sym = opt_trans in_scope co2' co1' - | otherwise = opt_trans in_scope co1' co2' + | sym = opt_trans opts in_scope co2' co1' + | otherwise = opt_trans opts in_scope co1' co2' + where - co1' = opt_co4_wrap env sym rep r co1 - co2' = opt_co4_wrap env sym rep r co2 + co1' = opt_co4_wrap "TransCo co1" opts env sym rep r co1 + co2' = opt_co4_wrap "TransCo co2" opts env sym rep r co2 in_scope = lcInScopeSet env -opt_co4 env _sym rep r (SelCo n co) +opt_co4 _ env _sym rep r (SelCo n co) | Just (ty, _co_role) <- isReflCo_maybe co = liftCoSubst (chooseRole rep r) env (getNthFromType n ty) -- NB: it is /not/ true that r = _co_role -- Rather, r = coercionRole (SelCo n co) -opt_co4 env sym rep r (SelCo (SelTyCon n r1) (TyConAppCo _ _ cos)) +opt_co4 opts env sym rep r (SelCo (SelTyCon n r1) (TyConAppCo _ _ cos)) = assert (r == r1 ) - opt_co4_wrap env sym rep r (cos `getNth` n) + opt_co4_wrap "SelTyCon" opts env sym rep r (cos `getNth` n) -- see the definition of GHC.Builtin.Types.Prim.funTyCon -opt_co4 env sym rep r (SelCo (SelFun fs) (FunCo _r2 _afl _afr w co1 co2)) - = opt_co4_wrap env sym rep r (getNthFun fs w co1 co2) +opt_co4 opts env sym rep r (SelCo (SelFun fs) (FunCo _r2 _afl _afr w co1 co2)) + = opt_co4_wrap "SelFun" opts env sym rep r (getNthFun fs w co1 co2) -opt_co4 env sym rep _ (SelCo SelForAll (ForAllCo _ eta _)) +opt_co4 opts env sym rep _ (SelCo SelForAll (ForAllCo _ eta _)) -- works for both tyvar and covar - = opt_co4_wrap env sym rep Nominal eta + = opt_co4_wrap "SelForAll" opts env sym rep Nominal eta -opt_co4 env sym rep r (SelCo n co) +opt_co4 opts env sym rep r (SelCo n co) | Just nth_co <- case (co', n) of (TyConAppCo _ _ cos, SelTyCon n _) -> Just (cos `getNth` n) (FunCo _ _ _ w co1 co2, SelFun fs) -> Just (getNthFun fs w co1 co2) @@ -387,71 +433,73 @@ opt_co4 env sym rep r (SelCo n co) _ -> Nothing = if rep && (r == Nominal) -- keep propagating the SubCo - then opt_co4_wrap (zapLiftingContext env) False True Nominal nth_co + then opt_co4_wrap "NthCo" opts (zapLiftingContext env) False True Nominal nth_co else nth_co | otherwise = wrapRole rep r $ SelCo n co' where - co' = opt_co1 env sym co + co' = opt_co1 opts env sym co -opt_co4 env sym rep r (LRCo lr co) +opt_co4 opts env sym rep r (LRCo lr co) | Just pr_co <- splitAppCo_maybe co = assert (r == Nominal ) - opt_co4_wrap env sym rep Nominal (pick_lr lr pr_co) + opt_co4_wrap "LrCO AppCo" opts env sym rep Nominal (pick_lr lr pr_co) | Just pr_co <- splitAppCo_maybe co' = assert (r == Nominal) $ if rep - then opt_co4_wrap (zapLiftingContext env) False True Nominal (pick_lr lr pr_co) + then opt_co4_wrap "LrCo AppCo'" opts (zapLiftingContext env) False True Nominal (pick_lr lr pr_co) else pick_lr lr pr_co | otherwise = wrapRole rep Nominal $ LRCo lr co' where - co' = opt_co4_wrap env sym False Nominal co + co' = opt_co4_wrap "LrCo co'" opts env sym False Nominal co pick_lr CLeft (l, _) = l pick_lr CRight (_, r) = r -- See Note [Optimising InstCo] -opt_co4 env sym rep r (InstCo co1 arg) +opt_co4 opts env sym rep r (InstCo co1 arg) -- forall over type... | Just (tv, kind_co, co_body) <- splitForAllCo_ty_maybe co1 - = opt_co4_wrap (extendLiftingContext env tv - (mkCoherenceRightCo Nominal t2 (mkSymCo kind_co) sym_arg)) - -- mkSymCo kind_co :: k1 ~ k2 - -- sym_arg :: (t1 :: k1) ~ (t2 :: k2) - -- tv |-> (t1 :: k1) ~ (((t2 :: k2) |> (sym kind_co)) :: k1) - sym rep r co_body + = opt_co4_wrap "InstCo ForAllTy" opts + (extendLiftingContext env tv + (mkCoherenceRightCo Nominal t2 (mkSymCo kind_co) sym_arg)) + -- mkSymCo kind_co :: k1 ~ k2 + -- sym_arg :: (t1 :: k1) ~ (t2 :: k2) + -- tv |-> (t1 :: k1) ~ (((t2 :: k2) |> (sym kind_co)) :: k1) + sym rep r co_body -- forall over coercion... | Just (cv, kind_co, co_body) <- splitForAllCo_co_maybe co1 , CoercionTy h1 <- t1 , CoercionTy h2 <- t2 - = let new_co = mk_new_co cv (opt_co4_wrap env sym False Nominal kind_co) h1 h2 - in opt_co4_wrap (extendLiftingContext env cv new_co) sym rep r co_body + = let new_co = mk_new_co cv (opt_co4_wrap "InstCo kind_co" opts env sym False Nominal kind_co) h1 h2 + in opt_co4_wrap "InstCo ForAllCo" opts (extendLiftingContext env cv new_co) sym rep r co_body -- See if it is a forall after optimization -- If so, do an inefficient one-variable substitution, then re-optimize -- forall over type... | Just (tv', kind_co', co_body') <- splitForAllCo_ty_maybe co1' - = opt_co4_wrap (extendLiftingContext (zapLiftingContext env) tv' - (mkCoherenceRightCo Nominal t2' (mkSymCo kind_co') arg')) - False False r' co_body' + = opt_co4_wrap "InstCo ForAllTy 2" opts + (extendLiftingContext (zapLiftingContext env) tv' + (mkCoherenceRightCo Nominal t2' (mkSymCo kind_co') arg')) + False False r' co_body' -- forall over coercion... | Just (cv', kind_co', co_body') <- splitForAllCo_co_maybe co1' , CoercionTy h1' <- t1' , CoercionTy h2' <- t2' = let new_co = mk_new_co cv' kind_co' h1' h2' - in opt_co4_wrap (extendLiftingContext (zapLiftingContext env) cv' new_co) + in opt_co4_wrap "InstCo ForAllCo 2" opts (extendLiftingContext (zapLiftingContext env) cv' new_co) False False r' co_body' | otherwise = InstCo co1' arg' where - co1' = opt_co4_wrap env sym rep r co1 + co1' = opt_co4_wrap "InstCo recur co1" opts env sym rep r co1 r' = chooseRole rep r - arg' = opt_co4_wrap env sym False Nominal arg + arg' = opt_co4_wrap "InstCo recur arg" opts env sym False Nominal arg sym_arg = wrapSym sym arg' -- Performance note: don't be alarmed by the two calls to coercionKind @@ -479,25 +527,28 @@ opt_co4 env sym rep r (InstCo co1 arg) in mkProofIrrelCo Nominal (Refl (coercionType h1)) h1 (n1 `mkTransCo` h2 `mkTransCo` (mkSymCo n2)) -opt_co4 env sym _rep r (KindCo co) +opt_co4 opts env sym _rep r (KindCo co) = assert (r == Nominal) $ let kco' = promoteCoercion co in case kco' of - KindCo co' -> promoteCoercion (opt_co1 env sym co') - _ -> opt_co4_wrap env sym False Nominal kco' + KindCo co' -> promoteCoercion (opt_co1 opts env sym co') + _ -> opt_co4_wrap "KindCo" opts env sym False Nominal kco' -- This might be able to be optimized more to do the promotion -- and substitution/optimization at the same time -opt_co4 env sym _ r (SubCo co) - = assert (r == Representational) $ - opt_co4_wrap env sym True Nominal co +opt_co4 opts env sym _ _r (SubCo co) + = assert (_r == Representational) $ + let res = opt_co4_wrap "SubCo" opts env sym True Nominal co + in case coercionRole res of + Nominal -> SubCo res + _ -> res -- This could perhaps be optimized more. -opt_co4 env sym rep r (AxiomRuleCo co cs) +opt_co4 opts env sym rep r (AxiomRuleCo co cs) = assert (r == coaxrRole co) $ wrapRole rep r $ wrapSym sym $ - AxiomRuleCo co (zipWith (opt_co2 env False) (coaxrAsmpRoles co) cs) + AxiomRuleCo co (zipWith (opt_co2 opts env False) (coaxrAsmpRoles co) cs) {- Note [Optimise CoVarCo to Refl] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -511,9 +562,9 @@ in GHC.Core.Coercion. ------------- -- | Optimize a phantom coercion. The input coercion may not necessarily -- be a phantom, but the output sure will be. -opt_phantom :: LiftingContext -> SymFlag -> Coercion -> NormalCo -opt_phantom env sym co - = opt_univ env sym (PhantomProv (mkKindCo co)) Phantom ty1 ty2 +opt_phantom :: OptCoParams -> LiftingContext -> SymFlag -> Coercion -> NormalCo +opt_phantom opts env sym co + = opt_univ Co opts env sym (PhantomProv (mkKindCo co)) Phantom ty1 ty2 where Pair ty1 ty2 = coercionKind co @@ -548,17 +599,41 @@ See #19509. -} -opt_univ :: LiftingContext -> SymFlag -> UnivCoProvenance -> Role - -> Type -> Type -> Coercion -opt_univ env sym (PhantomProv h) _r ty1 ty2 - | sym = mkPhantomCo h' ty2' ty1' - | otherwise = mkPhantomCo h' ty1' ty2' +type OptRes :: Data.Kind.Type -> Data.Kind.Type +type family OptRes co_or_dco where + OptRes Coercion = Coercion + OptRes DCoercion = ( Type, DCoercion ) + +type Optimiser in_co out_co = + OptCoParams -> LiftingContext -> SymFlag -> ReprFlag -> Role -> in_co -> out_co + +opt_co_or_dco :: CoOrDCo co_or_dco -> Type -> Optimiser co_or_dco co_or_dco +opt_co_or_dco Co _ = opt_co4 +opt_co_or_dco DCo l_ty = \ opts lc sym repr r dco -> + assert (sym == False) $ + snd $ + opt_dco4 opts lc repr r l_ty dco + +opt_univ :: forall co_or_dco + . Outputable co_or_dco + => CoOrDCo co_or_dco + -> OptCoParams + -> LiftingContext -> SymFlag -> UnivCoProvenance co_or_dco -> Role + -> Type -> Type -> OptRes co_or_dco +opt_univ co_or_dco opts env sym (PhantomProv h) _r ty1 ty2 + | sym = mk_phantom h' ty2' ty1' + | otherwise = mk_phantom h' ty1' ty2' where - h' = opt_co4 env sym False Nominal h + h' = wrap "opt_univ PhantomProv" (opt_co_or_dco co_or_dco ty1) opts env sym False Nominal h ty1' = substTy (lcSubstLeft env) ty1 ty2' = substTy (lcSubstRight env) ty2 -opt_univ env sym prov role oty1 oty2 + mk_phantom :: co_or_dco -> Type -> Type -> OptRes co_or_dco + mk_phantom = case co_or_dco of + Co -> mkPhantomCo + DCo -> \ h t1 t2 -> (t1, mkUnivDCo (PhantomProv h) t2) + +opt_univ co_or_dco opts env sym prov role oty1 oty2 | Just (tc1, tys1) <- splitTyConApp_maybe oty1 , Just (tc2, tys2) <- splitTyConApp_maybe oty2 , tc1 == tc2 @@ -567,10 +642,19 @@ opt_univ env sym prov role oty1 oty2 -- NB: prov must not be the two interesting ones (ProofIrrel & Phantom); -- Phantom is already taken care of, and ProofIrrel doesn't relate tyconapps = let roles = tyConRoleListX role tc1 - arg_cos = zipWith3 (mkUnivCo prov') roles tys1 tys2 - arg_cos' = zipWith (opt_co4 env sym False) roles arg_cos - in - mkTyConAppCo role tc1 arg_cos' + in case co_or_dco of + Co -> + let + arg_cos = zipWith3 mk_univ roles tys1 tys2 + arg_cos' = zipWith (opt_co4 opts env sym False) roles arg_cos + in + mkTyConAppCo role tc1 arg_cos' + DCo -> + let + arg_cos = zipWith3 (\ r x y -> snd $ mk_univ r x y) roles tys1 tys2 + (arg_lhs', arg_dcos') = unzip $ zipWith3 (opt_dco4 opts env False) roles tys1 arg_cos + in + (mkTyConApp tc1 arg_lhs', mkTyConAppDCo arg_dcos') -- can't optimize the AppTy case because we can't build the kind coercions. @@ -579,13 +663,16 @@ opt_univ env sym prov role oty1 oty2 -- NB: prov isn't interesting here either = let k1 = tyVarKind tv1 k2 = tyVarKind tv2 - eta = mkUnivCo prov' Nominal k1 k2 + eta = case co_or_dco of + Co -> mk_univ Nominal k1 k2 + DCo -> snd $ mk_univ Nominal k1 k2 + tv1' = mk_castTy (TyVarTy tv1) k1 eta k2 -- eta gets opt'ed soon, but not yet. - ty2' = substTyWith [tv2] [TyVarTy tv1 `mkCastTy` eta] ty2 + ty2' = substTyWith [tv2] [tv1'] ty2 - (env', tv1', eta') = optForAllCoBndr env sym tv1 eta + (env', tv1'', eta') = opt_forall tv1 eta in - mkForAllCo tv1' eta' (opt_univ env' sym prov' role ty1 ty2') + mk_forall tv1'' eta' (opt_univ co_or_dco opts env' sym prov' role ty1 ty2') | Just (cv1, ty1) <- splitForAllCoVar_maybe oty1 , Just (cv2, ty2) <- splitForAllCoVar_maybe oty2 @@ -593,17 +680,22 @@ opt_univ env sym prov role oty1 oty2 = let k1 = varType cv1 k2 = varType cv2 r' = coVarRole cv1 - eta = mkUnivCo prov' Nominal k1 k2 - eta_d = downgradeRole r' Nominal eta + eta = case co_or_dco of + Co -> mk_univ Nominal k1 k2 + DCo -> snd $ mk_univ Nominal k1 k2 + eta_d = downgradeRole r' Nominal $ + case co_or_dco of + Co -> eta + DCo -> mkHydrateDCo Nominal k1 eta k2 -- eta gets opt'ed soon, but not yet. n_co = (mkSymCo $ mkSelCo (SelTyCon 2 r') eta_d) `mkTransCo` (mkCoVarCo cv1) `mkTransCo` (mkSelCo (SelTyCon 3 r') eta_d) ty2' = substTyWithCoVars [cv2] [n_co] ty2 - (env', cv1', eta') = optForAllCoBndr env sym cv1 eta + (env', cv1', eta') = opt_forall cv1 eta in - mkForAllCo cv1' eta' (opt_univ env' sym prov' role ty1 ty2') + mk_forall cv1' eta' (opt_univ co_or_dco opts env' sym prov' role ty1 ty2') | otherwise = let ty1 = substTyUnchecked (lcSubstLeft env) oty1 @@ -611,87 +703,122 @@ opt_univ env sym prov role oty1 oty2 (a, b) | sym = (ty2, ty1) | otherwise = (ty1, ty2) in - mkUnivCo prov' role a b + mk_univ role a b where + mk_castTy :: Type -> Type -> co_or_dco -> Type -> Type + mk_castTy = case co_or_dco of + Co -> \ ty _ co _ -> CastTy ty co + DCo -> \ ty l dco r -> CastTy ty (mkHydrateDCo Nominal l dco r) + mk_univ :: Role -> Type -> Type -> OptRes co_or_dco + mk_univ = case co_or_dco of + Co -> mkUnivCo prov' + DCo -> \ _ l_ty r_ty -> (l_ty, mkUnivDCo prov' r_ty) + mk_forall :: TyCoVar -> co_or_dco -> OptRes co_or_dco -> OptRes co_or_dco + mk_forall cv eta = case co_or_dco of + Co -> mkForAllCo cv eta + DCo -> \ (_,body) -> (mkTyVarTy cv, mkForAllDCo cv eta body) + opt_forall :: TyCoVar -> co_or_dco -> (LiftingContext,TyCoVar,co_or_dco) + opt_forall tv co = case co_or_dco of + Co -> optForAllCoBndr opts env sym tv co + DCo -> optForAllDCoBndr opts env sym tv co + prov' :: UnivCoProvenance co_or_dco prov' = case prov of #if __GLASGOW_HASKELL__ < 901 -- This alt is redundant with the first match of the FunDef - PhantomProv kco -> PhantomProv $ opt_co4_wrap env sym False Nominal kco + PhantomProv kco -> PhantomProv + $ wrap "univ_co phantom" (opt_co_or_dco co_or_dco oty1) + opts env sym False Nominal kco #endif - ProofIrrelProv kco -> ProofIrrelProv $ opt_co4_wrap env sym False Nominal kco - PluginProv _ -> prov - CorePrepProv _ -> prov + ProofIrrelProv kco -> ProofIrrelProv + $ wrap "univ_co proof_irrel" (opt_co_or_dco co_or_dco oty1) + opts env sym False Nominal kco + PluginProv str -> PluginProv str + CorePrepProv homo -> CorePrepProv homo ------------- -opt_transList :: HasDebugCallStack => InScopeSet -> [NormalCo] -> [NormalCo] -> [NormalCo] -opt_transList is = zipWithEqual "opt_transList" (opt_trans is) +opt_transList :: HasDebugCallStack => OptCoParams -> InScopeSet -> [NormalCo] -> [NormalCo] -> [NormalCo] +opt_transList opts is = zipWithEqual "opt_transList" (opt_trans opts is) -- The input lists must have identical length. -opt_trans :: InScopeSet -> NormalCo -> NormalCo -> NormalCo -opt_trans is co1 co2 +opt_trans :: OptCoParams -> InScopeSet -> NormalCo -> NormalCo -> NormalCo +opt_trans opts is co1 co2 | isReflCo co1 = co2 -- optimize when co1 is a Refl Co - | otherwise = opt_trans1 is co1 co2 + | otherwise = opt_trans1 opts is co1 co2 -opt_trans1 :: InScopeSet -> NormalNonIdCo -> NormalCo -> NormalCo +opt_trans1 :: OptCoParams -> InScopeSet -> NormalNonIdCo -> NormalCo -> NormalCo -- First arg is not the identity -opt_trans1 is co1 co2 +opt_trans1 opts is co1 co2 | isReflCo co2 = co1 -- optimize when co2 is a Refl Co - | otherwise = opt_trans2 is co1 co2 + | otherwise = opt_trans2 opts is co1 co2 -opt_trans2 :: InScopeSet -> NormalNonIdCo -> NormalNonIdCo -> NormalCo +opt_trans2 :: OptCoParams -> InScopeSet -> NormalNonIdCo -> NormalNonIdCo -> NormalCo -- Neither arg is the identity -opt_trans2 is (TransCo co1a co1b) co2 +opt_trans2 opts is (TransCo co1a co1b) co2 -- Don't know whether the sub-coercions are the identity - = opt_trans is co1a (opt_trans is co1b co2) + = opt_trans opts is co1a (opt_trans opts is co1b co2) -opt_trans2 is co1 co2 - | Just co <- opt_trans_rule is co1 co2 +opt_trans2 opts is co1 co2 + | Just co <- opt_trans_rule opts is co1 co2 = co -opt_trans2 is co1 (TransCo co2a co2b) - | Just co1_2a <- opt_trans_rule is co1 co2a +opt_trans2 opts is co1 (TransCo co2a co2b) + | Just co1_2a <- opt_trans_rule opts is co1 co2a = if isReflCo co1_2a then co2b - else opt_trans1 is co1_2a co2b + else opt_trans1 opts is co1_2a co2b -opt_trans2 _ co1 co2 +opt_trans2 _ _ co1 co2 = mkTransCo co1 co2 ------ + -- Optimize coercions with a top-level use of transitivity. -opt_trans_rule :: InScopeSet -> NormalNonIdCo -> NormalNonIdCo -> Maybe NormalCo +opt_trans_rule :: OptCoParams -> InScopeSet -> NormalNonIdCo -> NormalNonIdCo -> Maybe NormalCo + +-- Handle a composition of two directed coercions. +opt_trans_rule opts is (HydrateDCo r lty1 dco1 _) (HydrateDCo _ lty2 dco2 rhs2) + = ( \ dco -> mkHydrateDCo r lty1 dco rhs2 ) + <$> opt_trans_rule_dco opts is r lty1 dco1 lty2 dco2 -opt_trans_rule is in_co1@(GRefl r1 t1 (MCo co1)) in_co2@(GRefl r2 _ (MCo co2)) +opt_trans_rule opts is (SymCo (HydrateDCo r lty1 dco1 rhs1)) (SymCo (HydrateDCo _ lty2 dco2 _)) + = ( \ dco -> mkSymCo $ mkHydrateDCo r lty2 dco rhs1 ) + <$> opt_trans_rule_dco opts is r lty2 dco2 lty1 dco1 + +-- When composing a Coercion with a DCoercion, we could imagine hydrating the DCoercion +-- a single step (e.g. using 'hydrateOneLayerDCo') to expose cancellation opportunities. +-- We don't do that for now. + +opt_trans_rule opts is in_co1@(GRefl r1 t1 (MCo co1)) in_co2@(GRefl r2 _ (MCo co2)) = assert (r1 == r2) $ fireTransRule "GRefl" in_co1 in_co2 $ - mkGReflRightCo r1 t1 (opt_trans is co1 co2) + mkGReflRightCo r1 t1 (opt_trans opts is co1 co2) -- Push transitivity through matching destructors -opt_trans_rule is in_co1@(SelCo d1 co1) in_co2@(SelCo d2 co2) +opt_trans_rule opts is in_co1@(SelCo d1 co1) in_co2@(SelCo d2 co2) | d1 == d2 , coercionRole co1 == coercionRole co2 , co1 `compatible_co` co2 = fireTransRule "PushNth" in_co1 in_co2 $ - mkSelCo d1 (opt_trans is co1 co2) + mkSelCo d1 (opt_trans opts is co1 co2) -opt_trans_rule is in_co1@(LRCo d1 co1) in_co2@(LRCo d2 co2) +opt_trans_rule opts is in_co1@(LRCo d1 co1) in_co2@(LRCo d2 co2) | d1 == d2 , co1 `compatible_co` co2 = fireTransRule "PushLR" in_co1 in_co2 $ - mkLRCo d1 (opt_trans is co1 co2) + mkLRCo d1 (opt_trans opts is co1 co2) -- Push transitivity inside instantiation -opt_trans_rule is in_co1@(InstCo co1 ty1) in_co2@(InstCo co2 ty2) +opt_trans_rule opts is in_co1@(InstCo co1 ty1) in_co2@(InstCo co2 ty2) | ty1 `eqCoercion` ty2 , co1 `compatible_co` co2 = fireTransRule "TrPushInst" in_co1 in_co2 $ - mkInstCo (opt_trans is co1 co2) ty1 + mkInstCo (opt_trans opts is co1 co2) ty1 -opt_trans_rule is in_co1@(UnivCo p1 r1 tyl1 _tyr1) - in_co2@(UnivCo p2 r2 _tyl2 tyr2) +opt_trans_rule opts is in_co1@(UnivCo p1 r1 tyl1 _tyr1) + in_co2@(UnivCo p2 r2 _tyl2 tyr2) | Just prov' <- opt_trans_prov p1 p2 = assert (r1 == r2) $ fireTransRule "UnivCo" in_co1 in_co2 $ @@ -699,54 +826,56 @@ opt_trans_rule is in_co1@(UnivCo p1 r1 tyl1 _tyr1) where -- if the provenances are different, opt'ing will be very confusing opt_trans_prov (PhantomProv kco1) (PhantomProv kco2) - = Just $ PhantomProv $ opt_trans is kco1 kco2 + = Just $ PhantomProv $ opt_trans opts is kco1 kco2 opt_trans_prov (ProofIrrelProv kco1) (ProofIrrelProv kco2) - = Just $ ProofIrrelProv $ opt_trans is kco1 kco2 - opt_trans_prov (PluginProv str1) (PluginProv str2) | str1 == str2 = Just p1 + = Just $ ProofIrrelProv $ opt_trans opts is kco1 kco2 + opt_trans_prov (PluginProv str1) (PluginProv str2) + | str1 == str2 + = Just p1 opt_trans_prov _ _ = Nothing -- Push transitivity down through matching top-level constructors. -opt_trans_rule is in_co1@(TyConAppCo r1 tc1 cos1) in_co2@(TyConAppCo r2 tc2 cos2) +opt_trans_rule opts is in_co1@(TyConAppCo r1 tc1 cos1) in_co2@(TyConAppCo r2 tc2 cos2) | tc1 == tc2 = assert (r1 == r2) $ fireTransRule "PushTyConApp" in_co1 in_co2 $ - mkTyConAppCo r1 tc1 (opt_transList is cos1 cos2) + mkTyConAppCo r1 tc1 (opt_transList opts is cos1 cos2) -opt_trans_rule is in_co1@(FunCo r1 afl1 afr1 w1 co1a co1b) - in_co2@(FunCo r2 afl2 afr2 w2 co2a co2b) +opt_trans_rule opts is in_co1@(FunCo r1 afl1 afr1 w1 co1a co1b) + in_co2@(FunCo r2 afl2 afr2 w2 co2a co2b) = assert (r1 == r2) $ -- Just like the TyConAppCo/TyConAppCo case assert (afr1 == afl2) $ fireTransRule "PushFun" in_co1 in_co2 $ - mkFunCo2 r1 afl1 afr2 (opt_trans is w1 w2) - (opt_trans is co1a co2a) - (opt_trans is co1b co2b) + mkFunCo2 r1 afl1 afr2 (opt_trans opts is w1 w2) + (opt_trans opts is co1a co2a) + (opt_trans opts is co1b co2b) -opt_trans_rule is in_co1@(AppCo co1a co1b) in_co2@(AppCo co2a co2b) +opt_trans_rule opts is in_co1@(AppCo co1a co1b) in_co2@(AppCo co2a co2b) -- Must call opt_trans_rule_app; see Note [EtaAppCo] - = opt_trans_rule_app is in_co1 in_co2 co1a [co1b] co2a [co2b] + = opt_trans_rule_app opts is in_co1 in_co2 co1a [co1b] co2a [co2b] -- Eta rules -opt_trans_rule is co1@(TyConAppCo r tc cos1) co2 +opt_trans_rule opts is co1@(TyConAppCo r tc cos1) co2 | Just cos2 <- etaTyConAppCo_maybe tc co2 = fireTransRule "EtaCompL" co1 co2 $ - mkTyConAppCo r tc (opt_transList is cos1 cos2) + mkTyConAppCo r tc (opt_transList opts is cos1 cos2) -opt_trans_rule is co1 co2@(TyConAppCo r tc cos2) +opt_trans_rule opts is co1 co2@(TyConAppCo r tc cos2) | Just cos1 <- etaTyConAppCo_maybe tc co1 = fireTransRule "EtaCompR" co1 co2 $ - mkTyConAppCo r tc (opt_transList is cos1 cos2) + mkTyConAppCo r tc (opt_transList opts is cos1 cos2) -opt_trans_rule is co1@(AppCo co1a co1b) co2 +opt_trans_rule opts is co1@(AppCo co1a co1b) co2 | Just (co2a,co2b) <- etaAppCo_maybe co2 - = opt_trans_rule_app is co1 co2 co1a [co1b] co2a [co2b] + = opt_trans_rule_app opts is co1 co2 co1a [co1b] co2a [co2b] -opt_trans_rule is co1 co2@(AppCo co2a co2b) +opt_trans_rule opts is co1 co2@(AppCo co2a co2b) | Just (co1a,co1b) <- etaAppCo_maybe co1 - = opt_trans_rule_app is co1 co2 co1a [co1b] co2a [co2b] + = opt_trans_rule_app opts is co1 co2 co1a [co1b] co2a [co2b] -- Push transitivity inside forall -- forall over types. -opt_trans_rule is co1 co2 +opt_trans_rule opts is co1 co2 | Just (tv1, eta1, r1) <- splitForAllCo_ty_maybe co1 , Just (tv2, eta2, r2) <- etaForAllCo_ty_maybe co2 = push_trans tv1 eta1 r1 tv2 eta2 r2 @@ -763,14 +892,14 @@ opt_trans_rule is co1 co2 -- Wanted: -- /\tv1 : (eta1;eta2). (r1; r2[tv2 |-> tv1 |> eta1]) = fireTransRule "EtaAllTy_ty" co1 co2 $ - mkForAllCo tv1 (opt_trans is eta1 eta2) (opt_trans is' r1 r2') + mkForAllCo tv1 (opt_trans opts is eta1 eta2) (opt_trans opts is' r1 r2') where is' = is `extendInScopeSet` tv1 r2' = substCoWithUnchecked [tv2] [mkCastTy (TyVarTy tv1) eta1] r2 -- Push transitivity inside forall -- forall over coercions. -opt_trans_rule is co1 co2 +opt_trans_rule opts is co1 co2 | Just (cv1, eta1, r1) <- splitForAllCo_co_maybe co1 , Just (cv2, eta2, r2) <- etaForAllCo_co_maybe co2 = push_trans cv1 eta1 r1 cv2 eta2 r2 @@ -789,7 +918,7 @@ opt_trans_rule is co1 co2 -- n2 = nth 3 eta1 -- nco = /\ cv1 : (eta1;eta2). (r1; r2[cv2 |-> (sym n1);cv1;n2]) = fireTransRule "EtaAllTy_co" co1 co2 $ - mkForAllCo cv1 (opt_trans is eta1 eta2) (opt_trans is' r1 r2') + mkForAllCo cv1 (opt_trans opts is eta1 eta2) (opt_trans opts is' r1 r2') where is' = is `extendInScopeSet` cv1 role = coVarRole cv1 @@ -801,7 +930,7 @@ opt_trans_rule is co1 co2 r2 -- Push transitivity inside axioms -opt_trans_rule is co1 co2 +opt_trans_rule opts is co1 co2 -- See Note [Push transitivity inside axioms] and -- Note [Push transitivity inside newtype axioms only] @@ -810,34 +939,34 @@ opt_trans_rule is co1 co2 , isNewTyCon (coAxiomTyCon con) , True <- sym , Just cos2 <- matchAxiom sym con ind co2 - , let newAxInst = AxiomInstCo con ind (opt_transList is (map mkSymCo cos2) cos1) + , let newAxInst = AxiomInstCo con ind (opt_transList opts is (map mkSymCo cos2) cos1) = fireTransRule "TrPushSymAxR" co1 co2 $ SymCo newAxInst - -- TrPushAxR + -- TrPushAxR (AxSuckR) | Just (sym, con, ind, cos1) <- co1_is_axiom_maybe , isNewTyCon (coAxiomTyCon con) , False <- sym , Just cos2 <- matchAxiom sym con ind co2 - , let newAxInst = AxiomInstCo con ind (opt_transList is cos1 cos2) + , let newAxInst = AxiomInstCo con ind (opt_transList opts is cos1 cos2) = fireTransRule "TrPushAxR" co1 co2 newAxInst - -- TrPushSymAxL + -- TrPushSymAxL (SymAxSuckL) | Just (sym, con, ind, cos2) <- co2_is_axiom_maybe , isNewTyCon (coAxiomTyCon con) , True <- sym , Just cos1 <- matchAxiom (not sym) con ind co1 - , let newAxInst = AxiomInstCo con ind (opt_transList is cos2 (map mkSymCo cos1)) + , let newAxInst = AxiomInstCo con ind (opt_transList opts is cos2 (map mkSymCo cos1)) = fireTransRule "TrPushSymAxL" co1 co2 $ SymCo newAxInst - -- TrPushAxL + -- TrPushAxL (AxSuckL) | Just (sym, con, ind, cos2) <- co2_is_axiom_maybe , isNewTyCon (coAxiomTyCon con) , False <- sym , Just cos1 <- matchAxiom (not sym) con ind co1 - , let newAxInst = AxiomInstCo con ind (opt_transList is cos1 cos2) + , let newAxInst = AxiomInstCo con ind (opt_transList opts is cos1 cos2) = fireTransRule "TrPushAxL" co1 co2 newAxInst - -- TrPushAxSym/TrPushSymAx + -- TrPushAxSym/TrPushSymAx (AxSym/SymAx) | Just (sym1, con1, ind1, cos1) <- co1_is_axiom_maybe , Just (sym2, con2, ind2, cos2) <- co2_is_axiom_maybe , con1 == con2 @@ -851,16 +980,16 @@ opt_trans_rule is co1 co2 , all (`elemVarSet` pivot_tvs) qtvs = fireTransRule "TrPushAxSym" co1 co2 $ if sym2 - -- TrPushAxSym - then liftCoSubstWith role qtvs (opt_transList is cos1 (map mkSymCo cos2)) lhs - -- TrPushSymAx - else liftCoSubstWith role qtvs (opt_transList is (map mkSymCo cos1) cos2) rhs + -- TrPushAxSym (AxSym) + then liftCoSubstWith role qtvs (opt_transList opts is cos1 (map mkSymCo cos2)) lhs + -- TrPushSymAx (SymAx) + else liftCoSubstWith role qtvs (opt_transList opts is (map mkSymCo cos1) cos2) rhs where co1_is_axiom_maybe = isAxiom_maybe co1 co2_is_axiom_maybe = isAxiom_maybe co2 role = coercionRole co1 -- should be the same as coercionRole co2! -opt_trans_rule _ co1 co2 -- Identity rule +opt_trans_rule _ _ co1 co2 -- Identity rule | let ty1 = coercionLKind co1 r = coercionRole co1 ty2 = coercionRKind co2 @@ -868,10 +997,11 @@ opt_trans_rule _ co1 co2 -- Identity rule = fireTransRule "RedTypeDirRefl" co1 co2 $ mkReflCo r ty2 -opt_trans_rule _ _ _ = Nothing +opt_trans_rule _ _ _ _ = Nothing -- See Note [EtaAppCo] -opt_trans_rule_app :: InScopeSet +opt_trans_rule_app :: OptCoParams + -> InScopeSet -> Coercion -- original left-hand coercion (printing only) -> Coercion -- original right-hand coercion (printing only) -> Coercion -- left-hand coercion "function" @@ -879,14 +1009,14 @@ opt_trans_rule_app :: InScopeSet -> Coercion -- right-hand coercion "function" -> [Coercion] -- right-hand coercion "args" -> Maybe Coercion -opt_trans_rule_app is orig_co1 orig_co2 co1a co1bs co2a co2bs +opt_trans_rule_app opts is orig_co1 orig_co2 co1a co1bs co2a co2bs | AppCo co1aa co1ab <- co1a , Just (co2aa, co2ab) <- etaAppCo_maybe co2a - = opt_trans_rule_app is orig_co1 orig_co2 co1aa (co1ab:co1bs) co2aa (co2ab:co2bs) + = opt_trans_rule_app opts is orig_co1 orig_co2 co1aa (co1ab:co1bs) co2aa (co2ab:co2bs) | AppCo co2aa co2ab <- co2a , Just (co1aa, co1ab) <- etaAppCo_maybe co1a - = opt_trans_rule_app is orig_co1 orig_co2 co1aa (co1ab:co1bs) co2aa (co2ab:co2bs) + = opt_trans_rule_app opts is orig_co1 orig_co2 co1aa (co1ab:co1bs) co2aa (co2ab:co2bs) | otherwise = assert (co1bs `equalLength` co2bs) $ @@ -907,12 +1037,224 @@ opt_trans_rule_app is orig_co1 orig_co2 co1a co1bs co2a co2bs co2bs' = zipWith3 mkGReflLeftCo rt2bs lt2bs kcobs co2bs'' = zipWith mkTransCo co2bs' co2bs in - mkAppCos (opt_trans is co1a co2a') - (zipWith (opt_trans is) co1bs co2bs'') + mkAppCos (opt_trans opts is co1a co2a') + (zipWith (opt_trans opts is) co1bs co2bs'') fireTransRule :: String -> Coercion -> Coercion -> Coercion -> Maybe Coercion fireTransRule _rule _co1 _co2 res - = Just res + = -- pprTrace _rule + -- (vcat [ text "co1:" <+> ppr _co1 + -- , text "co2:" <+> ppr _co2 + -- , text "res:" <+> ppr res ]) $ + Just res + +------ +-- Optimize directed coercions + +-- N.B.: The reason we return (Type, DCoercion) and not just DCoercion is that we +-- sometimes need the substituted LHS type (see opt_trans_dco). + +opt_phantom_dco :: OptCoParams -> LiftingContext -> Role -> Type -> DCoercion -> (Type, NormalDCo) +opt_phantom_dco opts env r l_ty dco = opt_univ DCo opts env False (PhantomProv kco) Phantom l_ty r_ty + where + kco = DehydrateCo (mkKindCo $ mkHydrateDCo r l_ty dco r_ty) + r_ty = followDCo r l_ty dco + -- A naive attempt at removing this entirely causes issues in test "type_in_type_hole_fits". + +opt_dco4_wrap :: String -> OptCoParams -> LiftingContext -> ReprFlag -> Role -> Type -> DCoercion -> (Type, NormalDCo) +opt_dco4_wrap str opts lc rep r l_ty dco = wrap ("opt_dco4 " ++ str) go opts lc False rep r dco + where + go opts lc _sym repr r dco = opt_dco4 opts lc repr r l_ty dco + +opt_dco2 :: OptCoParams + -> LiftingContext + -> Role -- ^ The role of the input coercion + -> Type + -> DCoercion -> (Type, NormalDCo) +opt_dco2 opts env Phantom ty dco = opt_phantom_dco opts env Phantom ty dco +opt_dco2 opts env r ty dco = opt_dco3 opts env Nothing r ty dco + +opt_dco3 :: OptCoParams -> LiftingContext -> Maybe Role -> Role -> Type -> DCoercion -> (Type, NormalDCo) +opt_dco3 opts env (Just Phantom) r ty dco = opt_phantom_dco opts env r ty dco +opt_dco3 opts env (Just Representational) r ty dco = opt_dco4_wrap "opt_dco3 R" opts env True r ty dco +opt_dco3 opts env _ r ty dco = opt_dco4_wrap "opt_dco3 _" opts env False r ty dco + +opt_dco4 :: OptCoParams -> LiftingContext -> ReprFlag -> Role -> Type -> DCoercion -> (Type, NormalDCo) +opt_dco4 opts env rep r l_ty dco = case dco of + + ReflDCo + -> lifted_dco + GReflRightDCo kco + | isGReflCo kco || isGReflCo kco' + -> lifted_dco + | otherwise + -> (l_ty', mkGReflRightDCo kco') + where + kco' = opt_co4 opts env False False Nominal kco + GReflLeftDCo kco + | isGReflCo kco || isGReflCo kco' + -> lifted_dco + | otherwise + -> (l_ty', mkGReflLeftDCo kco') + where + kco' = opt_co4 opts env False False Nominal kco + + TyConAppDCo dcos + | Just (tc, l_tys) <- splitTyConApp_maybe l_ty + -> let + (arg_ltys, arg_dcos) = + case (rep, r) of + (True, Nominal) -> + unzip $ + zipWith3 + (\ mb_r' -> opt_dco3 opts env mb_r' Nominal) + (map Just (tyConRoleListRepresentational tc)) + l_tys + dcos + (False, Nominal) -> + unzip $ + zipWith (opt_dco4 opts env False Nominal) l_tys dcos + (_, Representational) -> + unzip $ + zipWith3 + (opt_dco2 opts env) + (tyConRoleListRepresentational tc) + l_tys + dcos + (_, Phantom) -> pprPanic "opt_dco4 sees a phantom!" (ppr dco) + in (mkTyConApp tc arg_ltys, mkTyConAppDCo arg_dcos) + + | otherwise + -> pprPanic "opt_dco4: TyConAppDCo where ty is not a TyConApp" $ + vcat [ text "dco =" <+> ppr dco + , text "l_ty =" <+> ppr l_ty ] + + AppDCo dco1 dco2 + | Just (l_ty1, l_ty2) <- splitAppTy_maybe l_ty + , let + (l_ty1', l_dco1) = opt_dco4 opts env rep r l_ty1 dco1 + (l_ty2', l_dco2) = opt_dco4 opts env False Nominal l_ty2 dco2 + -> (mkAppTy l_ty1' l_ty2', mkAppDCo l_dco1 l_dco2) + | otherwise + -> pprPanic "opt_dco4: AppDCo where ty is not an AppTy" $ + vcat [ text "dco =" <+> ppr dco + , text "l_ty =" <+> ppr l_ty ] + + ForAllDCo dco_tcv k_dco body_dco + | ForAllTy (Bndr ty_tv af) body_ty <- coreFullView l_ty + -> case optForAllDCoBndr opts env False dco_tcv k_dco of + (env', dco_tcv', k_dco') -> + -- SLD TODO: can this be simplified? I made too many mistakes writing this... + let body_ty' = substTyWithInScope (lcInScopeSet env') [ty_tv] [mkTyVarTy dco_tcv'] body_ty + (body_ty'', body_dco') = opt_dco4_wrap "ForAllDCo" opts env' rep r body_ty' body_dco + forall_ty = mkForAllTy (Bndr dco_tcv' af) body_ty'' + forall_dco = mkForAllDCo dco_tcv' k_dco' body_dco' + forall_ty' = followDCo r forall_ty forall_dco + in (forall_ty, wrapRole_dco rep r forall_ty forall_dco forall_ty') + | otherwise + -> pprPanic "opt_dco4: ForAllDCo where ty is not a ForAllTy" $ + vcat [ text "dco =" <+> ppr dco + , text "l_ty =" <+> ppr l_ty ] + + CoVarDCo cv + -> let co' = opt_co4 opts env False rep r (CoVarCo cv) + in (coercionLKind co', mkDehydrateCo co') + + AxiomInstDCo {} + -> (l_ty', rep_dco) + StepsDCo {} + -> (l_ty', rep_dco) + + UnivDCo prov rhs_ty + -> opt_univ DCo opts env False prov r' l_ty rhs_ty + + TransDCo dco1 dco2 -> + let + (l_ty', dco1') = opt_dco4 opts env rep r l_ty dco1 + + -- Follow the original directed coercion, + -- to avoid applying the substitution twice. + mid_ty = followDCo r l_ty dco1 + (mid_ty', dco2') = opt_dco4 opts env rep r mid_ty dco2 + in + (l_ty', opt_trans_dco opts (lcInScopeSet env) r' l_ty' dco1' mid_ty' dco2') + + SubDCo dco -> + assert (r == Representational) $ + opt_dco4_wrap "SubDCo" opts env True Nominal l_ty dco + + DehydrateCo co -> + let co' = opt_co4_wrap "DehydrateCo" opts env False rep r co + in (coercionLKind co', mkDehydrateCo co') + + where + lifted_dco = let lifted_co = liftCoSubst r' env l_ty + in ( coercionLKind lifted_co, mkDehydrateCo lifted_co ) + l_ty' = substTyUnchecked (lcSubstLeft env) l_ty + r' = chooseRole rep r + rep_dco = wrapRole_dco rep r l_ty' dco (followDCo r l_ty' dco) + +--------------------------------------------------------- +-- Transitivity for directed coercions. + +opt_trans_dco :: OptCoParams -> InScopeSet -> Role -> Type -> NormalDCo -> Type -> NormalDCo -> NormalDCo +opt_trans_dco opts is r l_ty dco1 mid_ty dco2 + | isReflDCo dco1 = dco2 + -- optimize when dco1 is a Refl DCo + | otherwise = opt_trans1_dco opts is r l_ty dco1 mid_ty dco2 + +opt_trans1_dco :: OptCoParams -> InScopeSet -> Role -> Type -> NormalNonIdDCo -> Type -> NormalDCo -> NormalDCo +-- First arg is not the identity +opt_trans1_dco opts is r l_ty dco1 mid_ty dco2 + | isReflDCo dco2 = dco1 + -- optimize when co2 is a Refl Co + | otherwise = opt_trans2_dco opts is r l_ty dco1 mid_ty dco2 + +opt_trans2_dco :: OptCoParams -> InScopeSet -> Role -> Type -> NormalNonIdDCo -> Type -> NormalNonIdDCo -> NormalDCo +-- Neither arg is the identity +opt_trans2_dco opts is r l_ty (TransDCo dco1a dco1b) mid_ty dco2 + -- Don't know whether the sub-coercions are the identity + = let inner_ty = followDCo r l_ty dco1a + in opt_trans_dco opts is r l_ty dco1a inner_ty + (opt_trans_dco opts is r inner_ty dco1b mid_ty dco2) + + +opt_trans2_dco opts is r l_ty dco1 mid_ty dco2 + | Just co <- opt_trans_rule_dco opts is r l_ty dco1 mid_ty dco2 + = co + +opt_trans2_dco opts is r l_ty dco1 mid_ty (TransDCo dco2a dco2b) + | Just dco1_2a <- opt_trans_rule_dco opts is r l_ty dco1 mid_ty dco2a + = if isReflDCo dco1_2a + then dco2b + else + let inner_ty = followDCo r mid_ty dco1_2a + in opt_trans1_dco opts is r mid_ty dco1_2a inner_ty dco2b + +opt_trans2_dco _ _ _ _ dco1 _ dco2 + = mkTransDCo dco1 dco2 + +opt_trans_rule_dco :: OptCoParams -> InScopeSet -> Role -> Type -> NormalNonIdDCo -> Type -> NormalNonIdDCo -> Maybe NormalDCo + +-- Handle undirected coercions. +opt_trans_rule_dco opts is _ _ (DehydrateCo co1) _ (DehydrateCo co2) + = DehydrateCo <$> opt_trans_rule opts is co1 co2 + +opt_trans_rule_dco _ _ r l_ty dco1 mid_ty dco2 + | let r_ty = followDCo r mid_ty dco2 + , l_ty `eqType` r_ty + = fireTransRule_dco "RedTypeDirRefl" dco1 dco2 $ + mkReflDCo + +opt_trans_rule_dco _ _ _ _ _ _ _ = Nothing + +fireTransRule_dco :: String -> DCoercion -> DCoercion -> DCoercion -> Maybe DCoercion +fireTransRule_dco _rule _dco1 _dco2 res + = --pprTrace _rule + -- (vcat [ text "dco1:" <+> ppr _dco1 + -- , text "dco2:" <+> ppr _dco2 + -- , text "res:" <+> ppr res ]) $ + Just res {- Note [Push transitivity inside axioms] @@ -1092,12 +1434,21 @@ wrapSym sym co | sym = mkSymCo co | otherwise = co -- | Conditionally set a role to be representational -wrapRole :: ReprFlag +wrapRole :: HasDebugCallStack + => ReprFlag -> Role -- ^ current role -> Coercion -> Coercion wrapRole False _ = id wrapRole True current = downgradeRole Representational current +wrapRole_dco :: HasDebugCallStack + => ReprFlag + -> Role -- ^ current role + -> Type -> DCoercion -> Type + -> DCoercion +wrapRole_dco False _ _ dco _ = dco +wrapRole_dco True current l_ty dco r_ty = downgradeDCoToRepresentational current l_ty dco r_ty + -- | If we require a representational role, return that. Otherwise, -- return the "default" role provided. chooseRole :: ReprFlag @@ -1112,6 +1463,7 @@ isAxiom_maybe (SymCo co) | Just (sym, con, ind, cos) <- isAxiom_maybe co = Just (not sym, con, ind, cos) isAxiom_maybe (AxiomInstCo con ind cos) + | isNewTyCon (coAxiomTyCon con) -- Adam Gundry's special sauce = Just (False, con, ind, cos) isAxiom_maybe _ = Nothing @@ -1294,7 +1646,19 @@ and these two imply -} -optForAllCoBndr :: LiftingContext -> Bool +optForAllCoBndr :: OptCoParams + -> LiftingContext -> Bool -> TyCoVar -> Coercion -> (LiftingContext, TyCoVar, Coercion) -optForAllCoBndr env sym - = substForAllCoBndrUsingLC sym (opt_co4_wrap env sym False Nominal) env +optForAllCoBndr opts env sym + = substForAllCoBndrUsingLC sym + (substTyUnchecked (lcSubstLeft env)) + (opt_co4_wrap "optForAllCoBndr" opts env sym False Nominal) env + +optForAllDCoBndr :: OptCoParams + -> LiftingContext -> Bool + -> TyCoVar -> DCoercion -> (LiftingContext, TyCoVar, DCoercion) +optForAllDCoBndr opts env sym tv + = substForAllDCoBndrUsingLC sym + (substTyUnchecked (lcSubstLeft env)) + (snd . opt_dco4_wrap "optForAllDCoBndr" opts env False Nominal (tyVarKind tv)) env + tv |