summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Gundry <adam@well-typed.com>2023-05-13 18:43:16 +0200
committersheaf <sam.derbyshire@gmail.com>2023-05-13 18:43:16 +0200
commit6bb5d279e5132e73ad1823d645b10c451264aa78 (patch)
treefbee06f32e51b3b1a290f60dfdbddc4aa53c1bbe
parenta175170c5a124e952760d6aef5a50a4f038aeda2 (diff)
downloadhaskell-wip/amg/dcoercion.tar.gz
Directed coercionswip/amg/dcoercion
This patch introduces a slimmer version of coercions, directed coercions, which store fewer types within them. This more compact representation considerably speeds up programs which involve many type family reductions, as the coercion size no longer grows quadratically in the number of reduction steps. ------------------------- Metric Decrease: LargeRecord T12227 T12545 T13386 T3064 T5030 T8095 T9872a T9872b T9872b_defer T9872c T9872d Metric Increase: CoOpt_Singletons T18223 T9872a T9872b T9872c T9872d -------------------------
-rw-r--r--compiler/GHC/Core/Coercion.hs807
-rw-r--r--compiler/GHC/Core/Coercion.hs-boot16
-rw-r--r--compiler/GHC/Core/Coercion/Opt.hs802
-rw-r--r--compiler/GHC/Core/FVs.hs31
-rw-r--r--compiler/GHC/Core/FamInstEnv.hs104
-rw-r--r--compiler/GHC/Core/FamInstEnv.hs-boot9
-rw-r--r--compiler/GHC/Core/Lint.hs314
-rw-r--r--compiler/GHC/Core/Opt/Pipeline.hs2
-rw-r--r--compiler/GHC/Core/Opt/Simplify/Iteration.hs6
-rw-r--r--compiler/GHC/Core/Reduction.hs493
-rw-r--r--compiler/GHC/Core/SimpleOpt.hs3
-rw-r--r--compiler/GHC/Core/TyCo/FVs.hs211
-rw-r--r--compiler/GHC/Core/TyCo/Ppr.hs28
-rw-r--r--compiler/GHC/Core/TyCo/Ppr.hs-boot4
-rw-r--r--compiler/GHC/Core/TyCo/Rep.hs310
-rw-r--r--compiler/GHC/Core/TyCo/Rep.hs-boot3
-rw-r--r--compiler/GHC/Core/TyCo/Subst.hs180
-rw-r--r--compiler/GHC/Core/TyCo/Tidy.hs41
-rw-r--r--compiler/GHC/Core/Type.hs121
-rw-r--r--compiler/GHC/Core/Unify.hs-boot9
-rw-r--r--compiler/GHC/Core/Utils.hs4
-rw-r--r--compiler/GHC/CoreToIface.hs45
-rw-r--r--compiler/GHC/CoreToIface.hs-boot6
-rw-r--r--compiler/GHC/Driver/Config.hs10
-rw-r--r--compiler/GHC/Driver/Flags.hs1
-rw-r--r--compiler/GHC/Driver/Session.hs8
-rw-r--r--compiler/GHC/Iface/Rename.hs44
-rw-r--r--compiler/GHC/Iface/Syntax.hs36
-rw-r--r--compiler/GHC/Iface/Type.hs278
-rw-r--r--compiler/GHC/Iface/Type.hs-boot4
-rw-r--r--compiler/GHC/IfaceToCore.hs43
-rw-r--r--compiler/GHC/Tc/Gen/Foreign.hs32
-rw-r--r--compiler/GHC/Tc/Instance/Family.hs1
-rw-r--r--compiler/GHC/Tc/Solver/Canonical.hs33
-rw-r--r--compiler/GHC/Tc/Solver/Equality.hs88
-rw-r--r--compiler/GHC/Tc/Solver/Monad.hs26
-rw-r--r--compiler/GHC/Tc/Solver/Rewrite.hs352
-rw-r--r--compiler/GHC/Tc/TyCl/Utils.hs28
-rw-r--r--compiler/GHC/Tc/Types.hs4
-rw-r--r--compiler/GHC/Tc/Types/Evidence.hs2
-rw-r--r--compiler/GHC/Tc/Utils/TcMType.hs93
-rw-r--r--compiler/GHC/Tc/Utils/TcType.hs2
-rw-r--r--compiler/GHC/Tc/Utils/Unify.hs19
-rw-r--r--compiler/GHC/Types/Id/Make.hs9
-rw-r--r--docs/users_guide/using-optimisation.rst15
-rw-r--r--testsuite/tests/dcoercion/DCo_Array.hs31
-rw-r--r--testsuite/tests/dcoercion/DCo_Array_aux.hs21
-rw-r--r--testsuite/tests/dcoercion/DCo_Coercion.hs24
-rw-r--r--testsuite/tests/dcoercion/DCo_Hetero.hs25
-rw-r--r--testsuite/tests/dcoercion/DCo_Hetero.stderr4
-rw-r--r--testsuite/tests/dcoercion/DCo_HsBinds.hs34
-rw-r--r--testsuite/tests/dcoercion/DCo_HsType.hs47
-rw-r--r--testsuite/tests/dcoercion/DCo_InScope.hs37
-rw-r--r--testsuite/tests/dcoercion/DCo_LiftTyped.hs26
-rw-r--r--testsuite/tests/dcoercion/DCo_Phantom.hs35
-rw-r--r--testsuite/tests/dcoercion/DCo_PostProcess.hs28
-rw-r--r--testsuite/tests/dcoercion/DCo_Specialise.hs31
-rw-r--r--testsuite/tests/dcoercion/DCo_T15703_aux.hs48
-rw-r--r--testsuite/tests/dcoercion/DCo_TransOpt.hs108
-rw-r--r--testsuite/tests/dcoercion/DCo_TransOpt.stdout80
-rw-r--r--testsuite/tests/dcoercion/DCo_TypeRep.hs14
-rw-r--r--testsuite/tests/dcoercion/DCo_Typeable.hs28
-rw-r--r--testsuite/tests/dcoercion/Makefile3
-rw-r--r--testsuite/tests/dcoercion/all.T19
-rw-r--r--testsuite/tests/dependent/should_compile/T14729.stderr4
-rw-r--r--testsuite/tests/perf/compiler/all.T6
-rw-r--r--testsuite/tests/pmcheck/should_compile/T11195.hs2
-rw-r--r--testsuite/tests/roles/should_compile/Roles13.stderr29
-rw-r--r--testsuite/tests/tcplugins/RewritePlugin.hs9
69 files changed, 4228 insertions, 1142 deletions
diff --git a/compiler/GHC/Core/Coercion.hs b/compiler/GHC/Core/Coercion.hs
index 21d537112e..f9febf29c2 100644
--- a/compiler/GHC/Core/Coercion.hs
+++ b/compiler/GHC/Core/Coercion.hs
@@ -14,6 +14,7 @@
module GHC.Core.Coercion (
-- * Main data type
Coercion, CoercionN, CoercionR, CoercionP,
+ DCoercion(..), DCoercionN,
MCoercion(..), MCoercionN, MCoercionR,
CoSel(..), FunSel(..),
UnivCoProvenance, CoercionHole(..),
@@ -50,6 +51,26 @@ module GHC.Core.Coercion (
mkKindCo,
castCoercionKind, castCoercionKind1, castCoercionKind2,
+ mkReflDCo,
+ mkAppDCo,
+ mkAppDCos,
+ mkTyConAppDCo,
+ mkFunDCo,
+ mkForAllDCo,
+ mkHomoForAllDCos,
+ mkGReflLeftDCo,
+ mkGReflRightDCo,
+ mkCoherenceLeftDCo,
+ mkCoherenceRightDCo,
+ mkTransDCo, mkHydrateDCo,
+ followDCo, fullyHydrateDCo, hydrateOneLayerDCo,
+ expandDCo, expandAxiomInstDCo, expandOneStepDCo,
+ mkDehydrateCo,
+ mkCoVarDCo,
+ castDCoercionKind1, castDCoercionKind2,
+ mkUnivDCo, mkProofIrrelDCo,
+ mkSubDCo,
+
mkPrimEqPred, mkReprPrimEqPred, mkPrimEqPredRole,
mkNomPrimEqPred,
@@ -64,6 +85,7 @@ module GHC.Core.Coercion (
splitFunCo_maybe,
splitForAllCo_maybe,
splitForAllCo_ty_maybe, splitForAllCo_co_maybe,
+ splitForAllDCo_ty_maybe, splitForAllDCo_co_maybe,
tyConRole, tyConRolesX, tyConRolesRepresentational, setNominalRole_maybe,
tyConRoleListX, tyConRoleListRepresentational, funRole,
@@ -77,13 +99,15 @@ module GHC.Core.Coercion (
mkHomoForAllMCo, mkFunResMCo, mkPiMCos,
isReflMCo, checkReflexiveMCo,
+ isReflDCo, isReflexiveDCo,
+
-- ** Coercion variables
mkCoVar, isCoVar, coVarName, setCoVarName, setCoVarUnique,
-- ** Free variables
tyCoVarsOfCo, tyCoVarsOfCos, coVarsOfCo,
tyCoFVsOfCo, tyCoFVsOfCos, tyCoVarsOfCoDSet,
- coercionSize, anyFreeVarsOfCo,
+ coercionSize, anyFreeVarsOfCo, anyFreeVarsOfDCo,
-- ** Substitution
CvSubstEnv, emptyCvSubstEnv,
@@ -97,8 +121,10 @@ module GHC.Core.Coercion (
emptyLiftingContext, extendLiftingContext, extendLiftingContextAndInScope,
liftCoSubstVarBndrUsing, isMappedByLC,
+ mkLiftingContext,
mkSubstLiftingContext, zapLiftingContext,
- substForAllCoBndrUsingLC, lcSubst, lcInScopeSet,
+ substForAllCoBndrUsingLC, substForAllDCoBndrUsingLC,
+ lcSubst, lcInScopeSet,
LiftCoEnv, LiftingContext(..), liftEnvSubstLeft, liftEnvSubstRight,
substRightCo, substLeftCo, swapLiftCoEnv, lcSubstLeft, lcSubstRight,
@@ -121,6 +147,9 @@ module GHC.Core.Coercion (
-- * Other
promoteCoercion, buildCoercion,
+ downgradeRole_maybe,
+ downgradeDCoToRepresentational,
+
multToCo, mkRuntimeRepCo,
hasCoercionHoleTy, hasCoercionHoleCo, hasThisCoercionHoleTy,
@@ -143,6 +172,8 @@ import GHC.Core.Type
import GHC.Core.TyCon
import GHC.Core.TyCon.RecWalk
import GHC.Core.Coercion.Axiom
+import {-# SOURCE #-} GHC.Core.FamInstEnv ( chooseBranch )
+import {-# SOURCE #-} GHC.Core.Unify ( tcMatchTys )
import GHC.Types.Var
import GHC.Types.Var.Env
import GHC.Types.Var.Set
@@ -160,6 +191,7 @@ import GHC.Types.Unique.FM
import GHC.Data.List.Infinite (Infinite (..))
import qualified GHC.Data.List.Infinite as Inf
+import GHC.Utils.Monad
import GHC.Utils.Misc
import GHC.Utils.Outputable
import GHC.Utils.Panic
@@ -167,6 +199,8 @@ import GHC.Utils.Panic.Plain
import Control.Monad (foldM, zipWithM)
import Data.Function ( on )
+import Data.Functor.Identity (Identity(..))
+import Data.List ( zipWith4 )
import Data.Char( isDigit )
import qualified Data.Monoid as Monoid
@@ -571,6 +605,18 @@ splitForAllCo_co_maybe (ForAllCo cv k_co co)
| isCoVar cv = Just (cv, k_co, co)
splitForAllCo_co_maybe _ = Nothing
+-- | Like 'splitForAllCo_maybe', but only returns Just for tyvar binder
+splitForAllDCo_ty_maybe :: DCoercion -> Maybe (TyVar, DCoercion, DCoercion)
+splitForAllDCo_ty_maybe (ForAllDCo tv k_dco dco)
+ | isTyVar tv = Just (tv, k_dco, dco)
+splitForAllDCo_ty_maybe _ = Nothing
+
+-- | Like 'splitForAllCo_maybe', but only returns Just for covar binder
+splitForAllDCo_co_maybe :: DCoercion -> Maybe (CoVar, DCoercion, DCoercion)
+splitForAllDCo_co_maybe (ForAllDCo cv k_dco dco)
+ | isCoVar cv = Just (cv, k_dco, dco)
+splitForAllDCo_co_maybe _ = Nothing
+
-------------------------------------------------------
-- and some coercion kind stuff
@@ -593,7 +639,7 @@ coVarKindsTypesRole cv
coVarKind :: CoVar -> Type
coVarKind cv
- = assert (isCoVar cv )
+ = assert (isCoVar cv)
varType cv
coVarRole :: CoVar -> Role
@@ -663,6 +709,7 @@ isReflCoVar_maybe cv
isGReflCo :: Coercion -> Bool
isGReflCo (GRefl{}) = True
isGReflCo (Refl{}) = True -- Refl ty == GRefl N ty MRefl
+isGReflCo (HydrateDCo _ _ dco _) = isGReflDCo dco
isGReflCo _ = False
-- | Tests if this coercion is obviously reflexive. Guaranteed to work
@@ -671,6 +718,7 @@ isGReflCo _ = False
isReflCo :: Coercion -> Bool
isReflCo (Refl{}) = True
isReflCo (GRefl _ _ mco) | isGReflMCo mco = True
+isReflCo (HydrateDCo _ _ dco _) = isReflDCo dco
isReflCo _ = False
-- | Returns the type coerced if this coercion is a generalized reflexive
@@ -678,6 +726,9 @@ isReflCo _ = False
isGReflCo_maybe :: Coercion -> Maybe (Type, Role)
isGReflCo_maybe (GRefl r ty _) = Just (ty, r)
isGReflCo_maybe (Refl ty) = Just (ty, Nominal)
+isGReflCo_maybe (HydrateDCo r ty dco _)
+ | isGReflDCo dco
+ = Just (ty, r)
isGReflCo_maybe _ = Nothing
-- | Returns the type coerced if this coercion is reflexive. Guaranteed
@@ -686,6 +737,9 @@ isGReflCo_maybe _ = Nothing
isReflCo_maybe :: Coercion -> Maybe (Type, Role)
isReflCo_maybe (Refl ty) = Just (ty, Nominal)
isReflCo_maybe (GRefl r ty mco) | isGReflMCo mco = Just (ty, r)
+isReflCo_maybe (HydrateDCo r ty dco _)
+ | isReflDCo dco
+ = Just (ty, r)
isReflCo_maybe _ = Nothing
-- | Slowly checks if the coercion is reflexive. Don't call this in a loop,
@@ -696,15 +750,35 @@ isReflexiveCo = isJust . isReflexiveCo_maybe
-- | Extracts the coerced type from a reflexive coercion. This potentially
-- walks over the entire coercion, so avoid doing this in a loop.
isReflexiveCo_maybe :: Coercion -> Maybe (Type, Role)
-isReflexiveCo_maybe (Refl ty) = Just (ty, Nominal)
-isReflexiveCo_maybe (GRefl r ty mco) | isGReflMCo mco = Just (ty, r)
isReflexiveCo_maybe co
+ | Just res <- isReflCo_maybe co
+ = Just res
| ty1 `eqType` ty2
= Just (ty1, r)
| otherwise
= Nothing
where (Pair ty1 ty2, r) = coercionKindRole co
+isReflDCo :: DCoercion -> Bool
+isReflDCo ReflDCo = True
+isReflDCo (GReflRightDCo co) = isGReflCo co
+isReflDCo (GReflLeftDCo co) = isGReflCo co
+isReflDCo (DehydrateCo co) = isReflCo co
+isReflDCo _ = False
+
+isGReflDCo :: DCoercion -> Bool
+isGReflDCo ReflDCo = True
+isGReflDCo (GReflRightDCo {}) = True
+isGReflDCo (GReflLeftDCo {}) = True
+isGReflDCo (DehydrateCo co) = isGReflCo co
+isGReflDCo _ = False
+
+isReflexiveDCo :: Role -> Type -> DCoercion -> Type -> Bool
+isReflexiveDCo _r l_ty dco r_ty
+ | isReflDCo dco
+ = True
+ | otherwise
+ = l_ty `eqType` r_ty
{-
%************************************************************************
@@ -919,6 +993,493 @@ mkAppCos :: Coercion
-> Coercion
mkAppCos co1 cos = foldl' mkAppCo co1 cos
+mkReflDCo :: DCoercion
+mkReflDCo = ReflDCo
+
+mkTyConAppDCo :: [DCoercion] -> DCoercion
+mkTyConAppDCo cos
+ | all isReflDCo cos = mkReflDCo -- See Note [Refl invariant]
+ | otherwise = TyConAppDCo cos
+
+mkSubDCo :: HasDebugCallStack
+ => Type -- ^ LHS type
+ -> DCoercion
+ -> Type -- ^ RHS type
+ -> DCoercion
+mkSubDCo l_ty dco r_ty = case dco of
+ ReflDCo -> ReflDCo
+ GReflRightDCo co -> GReflRightDCo co
+ GReflLeftDCo co -> GReflLeftDCo co
+ TyConAppDCo dcos
+ | Just (tc, arg_l_tys) <- splitTyConApp_maybe l_ty
+ , Just (_ , arg_r_tys) <- splitTyConApp_maybe r_ty
+ -> TyConAppDCo (applyRoles_dco tc arg_l_tys dcos arg_r_tys)
+ -- SLD TODO: we might need to get rid of this case,
+ -- to avoid calling applyRoles, which calls mkHydrateDCo.
+ DehydrateCo co
+ -> DehydrateCo (mkSubCo co)
+ UnivDCo prov r
+ -> UnivDCo prov r
+ _ -> SubDCo dco
+
+-- | Like 'mkTyConAppDCo', but specialised to the function arrow.
+--
+-- Unlike for 'Coercion', for 'DCoercion' the function arrow does not
+-- have special treatment, so this is just a helper function around
+-- 'mkTyConAppDCo'.
+mkFunDCo :: FunTyFlag
+ -> DCoercionN -- ^ multiplicity
+ -> DCoercionN -- ^ argument representation
+ -> DCoercionN -- ^ result representation
+ -> DCoercion -- ^ argument
+ -> DCoercion -- ^ result
+ -> DCoercion
+mkFunDCo ftf w repco1 repco2 co1 co2 =
+ if isFUNArg ftf
+ then mkTyConAppDCo [w, repco1, repco2, co1, co2]
+ else mkTyConAppDCo [ repco1, repco2, co1, co2]
+
+mkAppDCo :: DCoercion -- ^ :: t1 ~r t2
+ -> DCoercion -- ^ :: s1 ~N s2, where s1 :: k1, s2 :: k2
+ -> DCoercion -- ^ :: t1 s1 ~r t2 s2
+mkAppDCo ReflDCo ReflDCo = ReflDCo
+mkAppDCo (TyConAppDCo args) arg = TyConAppDCo (args ++ [arg])
+mkAppDCo co arg = AppDCo co arg
+
+mkAppDCos :: DCoercion
+ -> [DCoercion]
+ -> DCoercion
+mkAppDCos co1 cos = foldl' mkAppDCo co1 cos
+
+-- | Transitivity for directed coercions.
+--
+-- Does some basic simplifications, i.e. either coercion is 'ReflDCo'
+-- or both are 'StepsDCo', but nothing more elaborate.
+mkTransDCo :: DCoercion -> DCoercion -> DCoercion
+ -- NB: if you change this function in an attempt to gain more simplification,
+ -- e.g. simplifying @StepsDCo n `mkTransCo` ( StepsDCo m ; dco )@ to
+ -- @StepsDCo (n+m) ; dco@, check it is not causing significant regressions
+ -- in the rewriter, e.g. T13386.
+mkTransDCo dco1 dco2
+ | isReflDCo dco1
+ = dco2
+ | isReflDCo dco2
+ = dco1
+-- SLD TODO: GRefl cases?
+mkTransDCo (StepsDCo n) (StepsDCo m)
+ = StepsDCo (n+m)
+mkTransDCo dco1 dco2
+ = TransDCo dco1 dco2
+
+-- | Make a Coercion from a tycovar, a kind coercion, and a body coercion.
+-- The kind of the tycovar should be the left-hand kind of the kind coercion.
+-- See Note [Unused coercion variable in ForAllCo]
+mkForAllDCo :: TyCoVar -> DCoercionN -> DCoercion -> DCoercion
+mkForAllDCo v kind_dco dco
+ | assert (isTyVar v || almostDevoidCoVarOfDCo v dco) True
+ , isReflDCo dco
+ , isGReflDCo kind_dco
+ = ReflDCo
+mkForAllDCo v kind_dco dco
+ = ForAllDCo v kind_dco dco
+
+-- | Like 'mkForAllCo', but the inner coercion shouldn't be an obvious
+-- reflexive coercion. For example, it is guaranteed in 'mkForAllCos'.
+-- The kind of the tycovar should be the left-hand kind of the kind coercion.
+mkForAllDCo_NoRefl :: TyCoVar -> DCoercionN -> TypeOrConstraint -> DCoercion -> DCoercion
+mkForAllDCo_NoRefl v kind_dco body_torc dco
+ | assert (isTyVar v || almostDevoidCoVarOfDCo v dco) True
+ , assert (not (isReflDCo dco)) True
+ , isCoVar v
+ , not (v `elemVarSet` tyCoVarsOfDCo dco)
+ = let var_torc = case sORTKind_maybe (tyVarKind v) of
+ Just (torc, _) -> torc
+ Nothing -> pprPanic "mkForAllDCo_NoRefl" (ppr v $$ ppr kind_dco)
+ ftf = mkFunTyFlag var_torc body_torc
+ in mkFunDCo ftf mkReflDCo mkReflDCo mkReflDCo kind_dco dco
+ -- Functions from coercions are always unrestricted
+ | otherwise
+ = ForAllDCo v kind_dco dco
+
+-- | Make a Coercion quantified over a type/coercion variable;
+-- the variable has the same type in both sides of the coercion
+mkHomoForAllDCos :: [TyCoVar] -> TypeOrConstraint -> DCoercion -> DCoercion
+mkHomoForAllDCos _ _ ReflDCo = ReflDCo
+mkHomoForAllDCos vs body_torc co = mkHomoForAllDCos_NoRefl vs body_torc co
+
+-- | Like 'mkHomoForAllCos', but the inner coercion shouldn't be an obvious
+-- reflexive coercion. For example, it is guaranteed in 'mkHomoForAllCos'.
+mkHomoForAllDCos_NoRefl :: [TyCoVar] -> TypeOrConstraint -> DCoercion -> DCoercion
+mkHomoForAllDCos_NoRefl vs body_torc orig_co
+ = assert (not (isReflDCo orig_co))
+ foldr go orig_co vs
+ where
+ go v co = mkForAllDCo_NoRefl v mkReflDCo body_torc co
+
+-- | Given @ty :: k1@, @co :: k1 ~ k2@,
+-- produces @co' :: ty ~r (ty |> co)@
+mkGReflRightDCo :: CoercionN -> DCoercion
+mkGReflRightDCo co
+ | isGReflCo co = mkReflDCo
+ -- the kinds of @k1@ and @k2@ are the same, thus @isGReflCo@
+ -- instead of @isReflCo@
+ | otherwise = GReflRightDCo co
+
+-- | Given @ty :: k1@, @co :: k1 ~ k2@,
+-- produces @co' :: (ty |> co) ~r ty@
+mkGReflLeftDCo :: CoercionN -> DCoercion
+mkGReflLeftDCo co
+ | isGReflCo co = mkReflDCo
+ -- the kinds of @k1@ and @k2@ are the same, thus @isGReflCo@
+ -- instead of @isReflCo@
+ | otherwise = GReflLeftDCo co
+
+-- | Given @ty :: k1@, @co :: k1 ~ k2@, @co2:: ty ~r ty'@,
+-- produces @co' :: (ty |> co) ~r ty'
+-- It is not only a utility function, but it saves allocation when co
+-- is a GRefl coercion.
+mkCoherenceLeftDCo :: CoercionN -> DCoercion -> DCoercion
+mkCoherenceLeftDCo co dco
+ | isGReflCo co = dco
+ | otherwise = GReflLeftDCo co `mkTransDCo` dco
+
+-- | Given @ty :: k1@, @co :: k1 ~ k2@, @co2:: ty' ~r ty@,
+-- produces @co' :: ty' ~r (ty |> co)
+-- It is not only a utility function, but it saves allocation when co
+-- is a GRefl coercion.
+mkCoherenceRightDCo :: CoercionN -> DCoercion -> DCoercion
+mkCoherenceRightDCo co dco
+ | isGReflCo co = dco
+ | otherwise = dco `mkTransDCo` GReflRightDCo co
+
+{- Note [Following a directed coercion]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Following a directed coercion means taking a directed coercion `dco`, a role `r`
+and a LHS (input) type `lhs`, and computing the RHS type of the directed coercion,
+`rhs`. This amounts to reconstituting a coercion `co :: lhs ~r rhs` from `dco`.
+
+This process however requires that lhs be sufficiently zonked. For example,
+if `dco = TyConAppDCo`, then we require that `lhs = TyConApp tc tys`, as we need
+to read off the `TyCon` from `lhs` in order to compute the `rhs`.
+
+To avoid any problems, we make sure we never call `followDCo` on an unzonked
+type; that is, we should not call this function from within the typechecker,
+when there are still metavariables floating around.
+-}
+
+-- | Turn a 'DCoercion' into a full 'Coercion' by specifying
+-- a 'Role' and the LHS and RHS 'Type's of the coercion.
+mkHydrateDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> Type -> Coercion
+mkHydrateDCo r l_ty dco r_ty =
+ -- NB: don't assert that r_ty = followDCo r l_ty dco,
+ -- as we sometimes call this function in the typechecker, which means that
+ -- l_ty might not be zonked, in which case followDCo could crash.
+ -- See Note [Following a directed coercion]
+ case dco of
+ ReflDCo -> mkReflCo r r_ty
+ CoVarDCo cv -> CoVarCo cv
+ DehydrateCo co -> let co_r = coercionRole co in
+ assertPpr (r == co_r)
+ (vcat [ text "mkHydrateCo: role mismatch"
+ , text "Expected:" <+> ppr r
+ , text " Actual:" <+> ppr co_r ])
+ $ co
+ _ -> HydrateDCo r l_ty dco r_ty
+
+
+fullyHydrateDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> Coercion
+fullyHydrateDCo r ty dco = fst $ expandDCo r ty dco
+
+hydrateOneLayerDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> Coercion
+hydrateOneLayerDCo r l_ty dco = fst $ expandDCoWith hydrate r l_ty dco
+ where
+ hydrate r l_ty dco
+ = let r_ty = followDCo r l_ty dco
+ co = mkHydrateDCo r l_ty dco r_ty
+ in (co, r_ty)
+
+followDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> Type
+followDCo r ty dco = snd $ expandDCo r ty dco
+
+expandDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> (Coercion, Type)
+expandDCo = expandDCoWith expandDCo
+
+expandDCoWith :: HasDebugCallStack
+ => (Role -> Type -> DCoercion -> (Coercion, Type))
+ -- ^ Function to use to recursively expand nested directed coercions
+ -> Role -> Type -> DCoercion -> (Coercion, Type)
+expandDCoWith _ r ty dco
+ | Just ty' <- coreView ty
+ = expandDCo r ty' dco
+
+expandDCoWith _ r l_ty ReflDCo
+ = (mkReflCo r l_ty, l_ty)
+
+expandDCoWith _ r l_ty (GReflRightDCo kco)
+ | let co = mkGReflCo r l_ty (coToMCo kco)
+ = (co, coercionRKind co)
+
+expandDCoWith _ r l_ty (GReflLeftDCo kco)
+ | let co = mkGReflCo r l_ty (mkSymMCo $ coToMCo kco)
+ -- N.B.: mkSymCo (mkGReflCo r l_ty mco) would be wrong,
+ -- because the LHS type of mco would not be the kind of l_ty.
+ = (co, coercionRKind co)
+
+expandDCoWith expander r l_ty dco@(TyConAppDCo dcos)
+ | Just (tc, l_tys) <- splitTyConApp_maybe l_ty
+ , let (cos, tys) = unzip $ zipWith3 expander (tyConRoleListX r tc) l_tys dcos
+ = (mkTyConAppCo r tc cos, mkTyConApp tc tys)
+ | otherwise
+ = pprPanic "expandDCo" (vcat [ text "TyConAppDCo where type is not a TyCon:"
+ , text "l_ty:" <+> ppr l_ty
+ , text "dco:" <+> ppr dco ])
+
+expandDCoWith expander r l_ty (AppDCo dco1 dco2)
+ | Just (l_ty1, l_ty2) <- splitAppTy_maybe l_ty
+ , let
+ (co1, ty1) = expander r l_ty1 dco1
+ r'
+ | Phantom <- r = Phantom
+ | otherwise = Nominal
+ (co2, ty2) = expander r' l_ty2 dco2
+ = (mkAppCo co1 co2, mkAppTy ty1 ty2)
+ | otherwise
+ = pprPanic "expandDCo" (text "AppDCo where type is not an AppTy:" <+> ppr l_ty)
+
+expandDCoWith expander r l_ty co@(ForAllDCo tcv kdco body_dco)
+ | not (isTyCoVar tcv)
+ = pprPanic "expandDCo" (text "Non tyco binder in ForAllDCo:" <+> ppr co)
+ | otherwise
+ = case coreFullView l_ty of
+ ForAllTy bndr body_ty
+ | (body_co, rhs_ty) <- expander r body_ty body_dco
+ , let (kco, _) = expandDCoWith expander Nominal (tyVarKind tcv) kdco
+ ->
+ (mkForAllCo tcv kco body_co
+ ,mkForAllTy bndr rhs_ty)
+ _ -> pprPanic "expandDCo" (text "ForAllDCo where type is not a ForAllTy:" <+> ppr l_ty <+> ppr co)
+
+expandDCoWith _ r _ (CoVarDCo cv)
+ = let cv_r = coVarRole cv in
+ assertPpr (r == cv_r)
+ (vcat [ text "expandDCo: wrong role"
+ , text "Expected:" <+> ppr r
+ , text " Actual:" <+> ppr cv_r
+ , text "cv:" <+> ppr cv ])
+ (CoVarCo cv, coVarRType cv)
+
+expandDCoWith _ r l_ty (AxiomInstDCo ax)
+ = runIdentity $ expandAxiomInstDCo assertPpr (pprPanic "expandDCo") r l_ty ax
+
+expandDCoWith _ r l_ty (StepsDCo 0)
+ = (mkReflCo r l_ty, l_ty)
+
+expandDCoWith expander r l_ty (StepsDCo n)
+ = let (co, ty) = runIdentity $ expandOneStepDCo assertPpr (pprPanic "expandDCo") r l_ty
+ (co', ty') = expandDCoWith expander r ty (StepsDCo (n-1))
+ in (co `mkTransCo` co', ty')
+
+expandDCoWith expander r l_ty (TransDCo dco1 dco2)
+ = let
+ (co1, ty1) = expander r l_ty dco1
+ (co2, ty2) = expander r ty1 dco2
+ in
+ (TransCo co1 co2, ty2)
+
+expandDCoWith expander r l_ty (SubDCo dco)
+ -- Keep expanding one more level through SubDCo.
+ = assert (r == Representational)
+ $ case expandDCoWith expander Nominal l_ty dco of
+ (co, rhs) -> (mkSubCo co, rhs)
+
+expandDCoWith _ r _ (DehydrateCo co)
+ = let co_r = coercionRole co in
+ assertPpr (r == co_r)
+ (vcat [ text "expandDCo: wrong role"
+ , text "Expected:" <+> ppr r
+ , text " Actual:" <+> ppr co_r
+ , text "co:" <+> ppr co ])
+ (co, coercionRKind co)
+
+expandDCoWith _ r l_ty (UnivDCo prov r_ty)
+ = (UnivCo (expandProv r l_ty prov) r l_ty r_ty, r_ty)
+
+-- | Expand an 'AxiomInstDCo' directed coercion by matching on an open type
+-- or data family instance. (Use 'expandOneStepDCo' for closed type families).
+--
+-- This function is used in both 'expandDCo' (which panics on failure)
+-- and in 'GHC.Core.Lint.lintDCoercion' (which errors in the 'LintM' monad).
+expandAxiomInstDCo :: (HasDebugCallStack, Applicative m)
+ => (Bool -> SDoc -> m (Coercion, Type) -> m (Coercion, Type))
+ -- ^ How to check assertions in @m@
+ -> (SDoc -> m (Coercion, Type))
+ -- ^ How to throw hard errors in @m@
+ -> Role -- ^ input role for expansion
+ -> Type -- ^ input LHS type for expansion
+ -> CoAxiom Branched -- ^ axiom to use
+ -> m (Coercion, Type)
+expandAxiomInstDCo check_prop throw_err r l_ty ax
+ | r == Phantom
+ = expandAxiomInstDCo check_prop throw_err Representational l_ty ax
+ -- AMG TODO: think about better fix to the above;
+ -- role could be Phantom because the coercion was downgraded,
+ -- maybe change the following to check role <= rather than exact matches
+
+ | otherwise
+ = case splitTyConApp_maybe l_ty of
+ Just (tc, tys)
+ | let (match_tys, other_tys) = splitAtList ax_lhs $ (map (\ x -> fromMaybe x $ coreView x) tys)
+ debug_info2 = debug_info $$
+ text "match_tys:" <+> ppr match_tys $$
+ text "other_tys:" <+> ppr other_tys
+ ->
+ case tcMatchTys ax_lhs match_tys of
+ Just subst ->
+ let inst_tys = substTyVars subst (coAxBranchTyVars branch) `chkAppend` other_tys
+ inst_cos = substCoVars subst (coAxBranchCoVars branch)
+ co = mkUnbranchedAxInstCo r ax' inst_tys inst_cos
+ in check_prop (tc_is_ok tc) (text "AxiomInstDCo: incorrect TyCon for Axiom" $$ debug_info2) $
+ pure (co, coercionRKind co)
+ Nothing ->
+ throw_err (text "AxiomInstDCo: couldn't match axiom" $$ debug_info2)
+ Nothing ->
+ throw_err (text "AxiomInstDCo: lhs not a TyConApp" $$ debug_info)
+ where
+ ax' = toUnbranchedAxiom ax
+ branch = coAxiomSingleBranch ax'
+ ax_lhs = coAxBranchLHS branch
+ tc_is_ok tc = coAxiomTyCon ax == tc &&
+ case r of
+ Representational -> isOpenFamilyTyCon tc
+ _ -> isOpenTypeFamilyTyCon tc
+ debug_info = vcat [ text "ax:" <+> ppr ax
+ , text "ax_lhs:" <+> ppr ax_lhs ]
+
+-- | Expand a @StepsDCo 1@ directed coercion by taking a single reduction step,
+-- matching on closed type family equations (and built-in type families), or
+-- unwrapping newtypes (not including data family newtype instances).
+-- (Use 'expandAxiomInstDCo' for open family axioms.)
+--
+-- This function is used in both 'expandDCo' (which panics on failure)
+-- and in 'GHC.Core.Lint.lintDCoercion' (which errors in the 'LintM' monad).
+expandOneStepDCo :: (HasDebugCallStack, Applicative m)
+ => (Bool -> SDoc -> m (Coercion, Type) -> m (Coercion, Type))
+ -- ^ How to check assertions in @m@
+ -> (SDoc -> m (Coercion, Type))
+ -- ^ How to throw hard errors in @m@
+ -> Role -- ^ input role for expansion
+ -> Type -- ^ input LHS type for expansion
+ -> m (Coercion, Type)
+expandOneStepDCo check_prop throw_err r l_ty
+ = case splitTyConApp_maybe l_ty of
+ Just (tc,tys)
+ -- Closed type family axioms.
+ | Just ax <- isClosedSynFamilyTyConWithAxiom_maybe tc
+ -> case chooseBranch ax tys of
+ Just (ind, inst_tys, inst_cos) ->
+ let
+ co = mkAxInstCo r ax ind inst_tys inst_cos
+ ty = coercionRKind co
+ in pure (co, ty)
+ Nothing ->
+ throw_err
+ (text "StepsDCo: couldn't choose branch" $$ debug_info2 $$ (text "ax:" <+> ppr ax))
+
+ -- Newtype axioms.
+ | Just (ty, co) <- instNewTyCon_maybe tc tys
+ , r == Representational
+ -> pure (co, ty)
+
+ -- Built-in type family axioms.
+ | Just sf <- isBuiltInSynFamTyCon_maybe tc
+ -> case sfMatchFam sf tys of
+ Just (ax, ts, ty) ->
+ let co = mkAxiomRuleCo ax (zipWith mkReflCo (coaxrAsmpRoles ax) ts)
+ in check_prop (r == coaxrRole ax)
+ (text "StepsDCo: axiom role mismatch" $$ debug_info2 $$ (text "ax:" <+> ppr ax))
+ $ pure (co, ty)
+ Nothing ->
+ throw_err (text "StepsDCo: couldn't match built-in axiom" $$ debug_info2)
+
+ -- Couldn't find any axiom associated to this TyCon.
+ | otherwise
+ -> throw_err (text "StepsDCo: no axiom to use" $$ debug_info2 $$ (text "tc:" <+> ppr tc))
+ where
+ debug_info2 = debug_info $$ (text "tys:" <+> ppr tys)
+
+ -- LHS type is not a TyConApp.
+ Nothing ->
+ throw_err (text "StepsDCo: LHS not a TyConApp" $$ debug_info)
+ where
+ debug_info = vcat [ text "r:" <+> ppr r
+ , text "l_ty:" <+> ppr l_ty ]
+
+expandProv :: HasDebugCallStack => Role -> Type -> UnivCoProvenance DCoercion -> UnivCoProvenance Coercion
+expandProv r l_ty (PhantomProv dco)
+ = assertPpr (r == Phantom) (text "expandProv PhantomProv")
+ $ PhantomProv (fullyHydrateDCo r l_ty dco)
+expandProv r l_ty (ProofIrrelProv dco)
+ = assertPpr (r /= Phantom) (text "expandProv ProofIrrelProv")
+ $ ProofIrrelProv (fullyHydrateDCo r l_ty dco)
+expandProv _ _ (PluginProv str)
+ = PluginProv str
+expandProv _ _ (CorePrepProv homo)
+ = CorePrepProv homo
+
+mkDehydrateCo :: Coercion -> DCoercion
+mkDehydrateCo co | isReflCo co = ReflDCo
+mkDehydrateCo (SymCo (GRefl _ _ MRefl))
+ = ReflDCo
+mkDehydrateCo (SymCo (GRefl _ _ (MCo co)))
+ = mkGReflLeftDCo co
+mkDehydrateCo (GRefl _ _ MRefl) = ReflDCo
+mkDehydrateCo (GRefl _ _ (MCo co)) = mkGReflRightDCo co
+mkDehydrateCo (HydrateDCo _ _ dco _) = dco
+--mkDehydrateCo (TyConAppCo _ _ cos)
+-- = mkTyConAppDCo $ map mkDehydrateCo cos
+--mkDehydrateCo (AppCo co1 co2)
+-- = mkAppDCo (mkDehydrateCo co1) (mkDehydrateCo co2)
+--mkDehydrateCo (ForAllCo tcv kind body)
+-- = mkForAllDCo tcv (mkDehydrateCo kind) (mkDehydrateCo body)
+mkDehydrateCo (AxiomInstCo coax _branch cos)
+ | all isReflCo cos -- AMG TODO: can we avoid the need for this check?
+ , isOpenFamilyTyCon (coAxiomTyCon coax)
+ = AxiomInstDCo coax
+ | all isReflCo cos
+ = singleStepDCo
+mkDehydrateCo (AxiomRuleCo _coax cos)
+ | all isReflCo cos -- AMG TODO: can we avoid the need for this check?
+ = singleStepDCo
+mkDehydrateCo (CoVarCo cv)
+ = CoVarDCo cv
+mkDehydrateCo (SubCo co)
+ = mkSubDCo (coercionLKind co) (mkDehydrateCo co) (coercionRKind co)
+--mkDehydrateCo (TransCo co1 co2)
+-- = mkTransDCo (mkDehydrateCo co1) (mkDehydrateCo co2)
+mkDehydrateCo co
+ = DehydrateCo co
+
+singleStepDCo :: DCoercion
+singleStepDCo = StepsDCo 1
+
+mkUnivDCo :: UnivCoProvenance DCoercion
+ -> Type -- RHS type
+ -> DCoercion
+mkUnivDCo = UnivDCo
+
+mkCoVarDCo :: CoVar -> DCoercion
+mkCoVarDCo v = CoVarDCo v
+
+mkProofIrrelDCo :: DCoercionN -> Type -> DCoercion
+
+-- if the two coercion prove the same fact, I just don't care what
+-- the individual coercions are.
+mkProofIrrelDCo dco rhs
+ | isGReflDCo dco
+ = mkReflDCo
+ | otherwise
+ = mkUnivDCo (ProofIrrelProv dco) rhs
+
{- Note [Unused coercion variable in ForAllCo]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See Note [Unused coercion variable in ForAllTy] in GHC.Core.TyCo.Rep for the
@@ -1112,7 +1673,7 @@ mkHoleCo :: CoercionHole -> Coercion
mkHoleCo h = HoleCo h
-- | Make a universal coercion between two arbitrary types.
-mkUnivCo :: UnivCoProvenance
+mkUnivCo :: UnivCoProvenance KindCoercion
-> Role -- ^ role of the built coercion, "r"
-> Type -- ^ t1 :: k1
-> Type -- ^ t2 :: k2
@@ -1131,6 +1692,8 @@ mkSymCo :: Coercion -> Coercion
mkSymCo co | isReflCo co = co
mkSymCo (SymCo co) = co
mkSymCo (SubCo (SymCo co)) = SubCo co
+mkSymCo (HydrateDCo r l_ty (GReflLeftDCo mco) r_ty) = HydrateDCo r r_ty (GReflRightDCo mco) l_ty
+mkSymCo (HydrateDCo r l_ty (GReflRightDCo mco) r_ty) = HydrateDCo r r_ty (GReflLeftDCo mco) l_ty
mkSymCo co = SymCo co
-- | Create a new 'Coercion' by composing the two given 'Coercion's transitively.
@@ -1140,7 +1703,10 @@ mkTransCo co1 co2 | isReflCo co1 = co2
| isReflCo co2 = co1
mkTransCo (GRefl r t1 (MCo co1)) (GRefl _ _ (MCo co2))
= GRefl r t1 (MCo $ mkTransCo co1 co2)
-mkTransCo co1 co2 = TransCo co1 co2
+mkTransCo (HydrateDCo r lhs dco1 _) (HydrateDCo _ _ dco2 rhs)
+ = mkHydrateDCo r lhs (mkTransDCo dco1 dco2) rhs
+mkTransCo dco1 dco2
+ = TransCo dco1 dco2
mkSelCo :: HasDebugCallStack
=> CoSel
@@ -1170,18 +1736,42 @@ mkSelCo_maybe cs co
-- If co :: (forall a1:t1 ~ t2. t1) ~ (forall a2:t3 ~ t4. t2)
-- then (nth SelForAll co :: (t1 ~ t2) ~N (t3 ~ t4))
+ go SelForAll dco@(HydrateDCo _ _ (ForAllDCo tv kind_co _) rhs)
+ = case splitForAllTyCoVar_maybe rhs of
+ Just (tv', _) -> Just $
+ mkHydrateDCo Nominal (tyVarKind tv) kind_co (tyVarKind tv')
+ _ -> pprPanic "mkSelCo_maybe" (ppr dco $$ ppr rhs)
+
go (SelFun fs) (FunCo _ _ _ w arg res)
= Just (getNthFun fs w arg res)
+ -- no FunDCo
+
go (SelTyCon i r) (TyConAppCo r0 tc arg_cos)
= assertPpr (r == tyConRole r0 tc i)
(vcat [ ppr tc, ppr arg_cos, ppr r0, ppr i, ppr r ]) $
Just (arg_cos `getNth` i)
+ go (SelTyCon n r) (HydrateDCo r0 l_ty (TyConAppDCo arg_dcos) r_ty)
+ | Just (tc, arg_tys) <- splitTyConApp_maybe l_ty
+ , Just (_, rhs_tys) <- splitTyConApp_maybe r_ty
+ = assertPpr (r == tyConRole r0 tc n)
+ (vcat [ text "tc:" <+> ppr tc
+ , text "arg_dcos:" <+> ppr arg_dcos
+ , text "r0:" <+> ppr r0
+ , text "n:" <+> ppr n
+ , text "r:" <+> ppr r ]) $
+ Just $ mkHydrateDCo
+ (tyConRole r0 tc n)
+ (arg_tys `getNth` n)
+ (arg_dcos `getNth` n)
+ (rhs_tys `getNth` n)
+
go cs (SymCo co) -- Recurse, hoping to get to a TyConAppCo or FunCo
= do { co' <- go cs co; return (mkSymCo co') }
- go _ _ = Nothing
+ go _ _
+ = Nothing
-- Assertion checking
bad_call_msg = vcat [ text "Coercion =" <+> ppr co
@@ -1194,7 +1784,7 @@ mkSelCo_maybe cs co
good_call SelForAll
| Just (_tv1, _) <- splitForAllTyCoVar_maybe ty1
, Just (_tv2, _) <- splitForAllTyCoVar_maybe ty2
- = True
+ = True
good_call (SelFun {})
= isFunTy ty1 && isFunTy ty2
@@ -1226,6 +1816,13 @@ mkLRCo :: LeftOrRight -> Coercion -> Coercion
mkLRCo lr co
| Just (ty, eq) <- isReflCo_maybe co
= mkReflCo eq (pickLR lr (splitAppTy ty))
+ | AppCo l r <- co
+ = pickLR lr (l,r)
+ | HydrateDCo r l_ty (AppDCo dco1 dco2) r_ty <- co
+ , Just (l_ty_1, l_ty_2) <- splitAppTy_maybe l_ty
+ , Just (r_ty_1, r_ty_2) <- splitAppTy_maybe r_ty
+ = pickLR lr ( mkHydrateDCo r l_ty_1 dco1 r_ty_1
+ , mkHydrateDCo Nominal l_ty_2 dco2 r_ty_2 )
| otherwise
= LRCo lr co
@@ -1277,8 +1874,14 @@ mkCoherenceRightCo r ty co co2
mkKindCo :: Coercion -> Coercion
mkKindCo co | Just (ty, _) <- isReflCo_maybe co = Refl (typeKind ty)
mkKindCo (GRefl _ _ (MCo co)) = co
+mkKindCo (HydrateDCo _ _ (GReflRightDCo co) _) = co
+mkKindCo (HydrateDCo _ _ (GReflLeftDCo co) _) = mkSymCo co
mkKindCo (UnivCo (PhantomProv h) _ _ _) = h
mkKindCo (UnivCo (ProofIrrelProv h) _ _ _) = h
+mkKindCo (HydrateDCo _ lhs (UnivDCo (PhantomProv h) rhs) _)
+ = mkHydrateDCo Nominal (typeKind lhs) h (typeKind rhs)
+mkKindCo (HydrateDCo _ lhs (UnivDCo (ProofIrrelProv h) rhs) _)
+ = mkHydrateDCo Nominal (typeKind lhs) h (typeKind rhs)
mkKindCo co
| Pair ty1 ty2 <- coercionKind co
-- generally, calling coercionKind during coercion creation is a bad idea,
@@ -1302,11 +1905,29 @@ mkSubCo co@(FunCo { fco_role = Nominal, fco_arg = arg, fco_res = res })
= co { fco_role = Representational
, fco_arg = downgradeRole Representational Nominal arg
, fco_res = downgradeRole Representational Nominal res }
+mkSubCo (UnivCo p Nominal t1 t2) = UnivCo p Representational t1 t2
+mkSubCo (HydrateDCo _r l_ty dco r_ty)
+ = assertPpr (_r == Nominal)
+ (vcat [ text "mkSubCo (HydrateDCo): unexpected role " <+> ppr _r
+ , text "l_ty:" <+> ppr l_ty
+ , text "dco:" <+> ppr dco ])
+ $ mkHydrateDCo Representational l_ty (mkSubDCo l_ty dco r_ty) r_ty
mkSubCo co = assertPpr (coercionRole co == Nominal) (ppr co <+> ppr (coercionRole co)) $
SubCo co
+downgradeDCoToRepresentational :: Role -- ^ Role of input 'DCoercion'
+ -> Type -- ^ LHS type of coercion
+ -> DCoercion
+ -> Type -- ^ RHS type of coercion
+ -> DCoercion
+downgradeDCoToRepresentational Nominal lhs dco rhs = mkSubDCo lhs dco rhs
+downgradeDCoToRepresentational Representational _ dco _ = dco
+downgradeDCoToRepresentational Phantom _ dco _
+ = pprPanic "downgradeToRepresentationalDCo: Phantom" (ppr dco)
+
-- | Changes a role, but only a downgrade. See Note [Role twiddling functions]
-downgradeRole_maybe :: Role -- ^ desired role
+downgradeRole_maybe :: HasDebugCallStack
+ => Role -- ^ desired role
-> Role -- ^ current role
-> Coercion -> Maybe Coercion
-- In (downgradeRole_maybe dr cr co) it's a precondition that
@@ -1324,7 +1945,8 @@ downgradeRole_maybe Phantom _ co = Just (toPhantomCo co)
-- | Like 'downgradeRole_maybe', but panics if the change isn't a downgrade.
-- See Note [Role twiddling functions]
-downgradeRole :: Role -- desired role
+downgradeRole :: HasDebugCallStack
+ => Role -- desired role
-> Role -- current role
-> Coercion -> Coercion
downgradeRole r1 r2 co
@@ -1344,10 +1966,12 @@ mkProofIrrelCo :: Role -- ^ role of the created coercion, "r"
-- if the two coercion prove the same fact, I just don't care what
-- the individual coercions are.
-mkProofIrrelCo r co g _ | isGReflCo co = mkReflCo r (mkCoercionTy g)
+mkProofIrrelCo r co g _
+ | isGReflCo co = mkReflCo r (mkCoercionTy g)
-- kco is a kind coercion, thus @isGReflCo@ rather than @isReflCo@
-mkProofIrrelCo r kco g1 g2 = mkUnivCo (ProofIrrelProv kco) r
- (mkCoercionTy g1) (mkCoercionTy g2)
+mkProofIrrelCo r kco g1 g2
+ = mkUnivCo (ProofIrrelProv kco) r
+ (mkCoercionTy g1) (mkCoercionTy g2)
{-
%************************************************************************
@@ -1400,14 +2024,54 @@ setNominalRole_maybe r co
pprPanic "setNominalRole_maybe: the coercion should already be nominal" (ppr co)
setNominalRole_maybe_helper (InstCo co arg)
= InstCo <$> setNominalRole_maybe_helper co <*> pure arg
+ setNominalRole_maybe_helper (HydrateDCo r ty1 dco mrty)
+ = (\ d -> HydrateDCo Nominal ty1 d mrty) <$> setNominalRole_maybe_dco r ty1 dco
setNominalRole_maybe_helper (UnivCo prov _ co1 co2)
- | case prov of PhantomProv _ -> False -- should always be phantom
- ProofIrrelProv _ -> True -- it's always safe
- PluginProv _ -> False -- who knows? This choice is conservative.
- CorePrepProv _ -> True
- = Just $ UnivCo prov Nominal co1 co2
+ | Just prov' <- setNominalRole_maybe_prov prov
+ = Just $ UnivCo prov' Nominal co1 co2
setNominalRole_maybe_helper _ = Nothing
+setNominalRole_maybe_dco :: Role -> Type -> DCoercion -> Maybe DCoercion
+setNominalRole_maybe_dco _ _ dco@ReflDCo = pure dco
+setNominalRole_maybe_dco _ _ dco@GReflRightDCo{} = pure dco
+setNominalRole_maybe_dco _ _ dco@GReflLeftDCo{} = pure dco
+setNominalRole_maybe_dco _ ty (TyConAppDCo dcos)
+ = do { let (tc, tys) = splitTyConApp ty
+ ; dcos' <- zipWith3M setNominalRole_maybe_dco (tyConRoleListX Representational tc) tys dcos
+ ; return $ TyConAppDCo dcos' }
+setNominalRole_maybe_dco r ty (AppDCo dco1 dco2)
+ = do { let (ty1, _) = splitAppTy ty
+ ; AppDCo <$> setNominalRole_maybe_dco r ty1 dco1 <*> pure dco2
+ }
+setNominalRole_maybe_dco r ty (ForAllDCo tv kind_co dco)
+ = do { let (_, body_ty) = splitForAllTyCoVar ty
+ ; ForAllDCo tv kind_co <$> setNominalRole_maybe_dco r body_ty dco
+ }
+setNominalRole_maybe_dco _ _ CoVarDCo{} = Nothing
+setNominalRole_maybe_dco _ _ dco@(AxiomInstDCo coax)
+ | coAxiomRole coax == Nominal = pure dco
+ | otherwise = Nothing
+setNominalRole_maybe_dco _ _ StepsDCo{} = Nothing
+setNominalRole_maybe_dco r ty (TransDCo dco1 dco2)
+ = TransDCo <$> setNominalRole_maybe_dco r ty dco1 <*> setNominalRole_maybe_dco r mid_ty dco2
+ where
+ mid_ty = followDCo r ty dco1
+ -- OK to call followDCo here: this function is always called on fully zonked types.
+setNominalRole_maybe_dco _ _ (SubDCo dco) = Just dco
+setNominalRole_maybe_dco r _ (DehydrateCo co) = DehydrateCo <$> setNominalRole_maybe r co
+setNominalRole_maybe_dco _ _ (UnivDCo prov rhs)
+ | Just prov' <- setNominalRole_maybe_prov prov
+ = Just $ UnivDCo prov' rhs
+ | otherwise
+ = Nothing
+
+setNominalRole_maybe_prov :: UnivCoProvenance co -> Maybe (UnivCoProvenance co)
+setNominalRole_maybe_prov prov = case prov of
+ PhantomProv _ -> Nothing -- should always be phantom
+ ProofIrrelProv _ -> Just prov -- it's always safe
+ PluginProv _ -> Nothing -- who knows? This choice is conservative.
+ CorePrepProv _ -> Just prov
+
-- | Make a phantom coercion between two types. The coercion passed
-- in must be a nominal coercion between the kinds of the
-- types.
@@ -1426,6 +2090,17 @@ applyRoles :: TyCon -> [Coercion] -> [Coercion]
applyRoles = zipWith (`downgradeRole` Nominal) . tyConRoleListRepresentational
-- The Role parameter is the Role of the TyConAppCo
+applyRoles_dco :: TyCon -> [Type] -> [DCoercion] -> [Type] -> [DCoercion]
+applyRoles_dco tc l_tys dcos r_tys
+ = zipWith4 downgrade (tyConRoleListRepresentational tc) l_tys dcos r_tys
+ where
+ downgrade r l_ty dco r_ty = case r of
+ Nominal -> dco
+ Representational -> mkSubDCo l_ty dco r_ty
+ Phantom -> mkDehydrateCo $ mkPhantomCo (mkKindCo co) l_ty r_ty
+ where
+ co = mkHydrateDCo Nominal l_ty dco r_ty
+
-- defined here because this is intimately concerned with the implementation
-- of TyConAppCo
-- Always returns an infinite list (with a infinite tail of Nominal)
@@ -1532,6 +2207,8 @@ promoteCoercion co = case co of
UnivCo (PluginProv _) _ _ _ -> mkKindCo co
UnivCo (CorePrepProv _) _ _ _ -> mkKindCo co
+ HydrateDCo {} -> mkKindCo co
+
SymCo g
-> mkSymCo (promoteCoercion g)
@@ -1613,17 +2290,32 @@ instCoercions g ws
= do { g' <- instCoercion g_tys g w
; return (piResultTy <$> g_tys <*> w_tys, g') }
+castDCoercionKind2 :: DCoercion -> CoercionN -> CoercionN -> DCoercion
+castDCoercionKind2 g h1 h2
+ = mkCoherenceRightDCo h2 (mkCoherenceLeftDCo h1 g)
+
+castDCoercionKind1 :: DCoercion -> CoercionN -> DCoercion
+castDCoercionKind1 g h
+ = case g of
+ ReflDCo -> ReflDCo
+ GReflRightDCo kind_co -> GReflRightDCo $
+ mkSymCo h `mkTransCo` kind_co `mkTransCo` h
+ GReflLeftDCo kind_co -> GReflLeftDCo $
+ mkSymCo h `mkTransCo` kind_co `mkTransCo` h
+ _ -> castDCoercionKind2 g h h
+
+
-- | Creates a new coercion with both of its types casted by different casts
-- @castCoercionKind2 g r t1 t2 h1 h2@, where @g :: t1 ~r t2@,
-- has type @(t1 |> h1) ~r (t2 |> h2)@.
-- @h1@ and @h2@ must be nominal.
castCoercionKind2 :: Coercion -> Role -> Type -> Type
- -> CoercionN -> CoercionN -> Coercion
+ -> CoercionN -> CoercionN -> Coercion
castCoercionKind2 g r t1 t2 h1 h2
= mkCoherenceRightCo r t2 h2 (mkCoherenceLeftCo r t1 h1 g)
--- | @castCoercionKind1 g r t1 t2 h@ = @coercionKind g r t1 t2 h h@
--- That is, it's a specialised form of castCoercionKind, where the two
+-- | @castCoercionKind1 g r t1 t2 h@ = @castCoercionKind2 g r t1 t2 h h@
+-- That is, it's a specialised form of castCoercionKind2, where the two
-- kind coercions are identical
-- @castCoercionKind1 g r t1 t2 h@, where @g :: t1 ~r t2@,
-- has type @(t1 |> h) ~r (t2 |> h)@.
@@ -1950,7 +2642,8 @@ type LiftCoEnv = VarEnv Coercion
-- Also maps coercion variables to ProofIrrelCos.
-- like liftCoSubstWith, but allows for existentially-bound types as well
-liftCoSubstWithEx :: Role -- desired role for output coercion
+liftCoSubstWithEx :: HasDebugCallStack
+ => Role -- desired role for output coercion
-> [TyVar] -- universally quantified tyvars
-> [Coercion] -- coercions to substitute for those
-> [TyCoVar] -- existentially quantified tycovars
@@ -2054,13 +2747,25 @@ zapLiftingContext (LC subst _) = LC (zapSubst subst) emptyVarEnv
-- | Like 'substForAllCoBndr', but works on a lifting context
substForAllCoBndrUsingLC :: Bool
- -> (Coercion -> Coercion)
- -> LiftingContext -> TyCoVar -> Coercion
- -> (LiftingContext, TyCoVar, Coercion)
-substForAllCoBndrUsingLC sym sco (LC subst lc_env) tv co
+ -> (Type -> Type)
+ -> (Coercion -> Coercion)
+ -> LiftingContext -> TyCoVar -> Coercion
+ -> (LiftingContext, TyCoVar, Coercion)
+substForAllCoBndrUsingLC sym sty sco (LC subst lc_env) tv co
+ = (LC subst' lc_env, tv', co')
+ where
+ (subst', tv', co') = substForAllCoBndrUsing Co sym sty sco subst tv co
+
+-- | Like 'substForAllDCoBndr', but works on a lifting context
+substForAllDCoBndrUsingLC :: Bool
+ -> (Type -> Type)
+ -> (DCoercion -> DCoercion)
+ -> LiftingContext -> TyCoVar -> DCoercion
+ -> (LiftingContext, TyCoVar, DCoercion)
+substForAllDCoBndrUsingLC sym sty sco (LC subst lc_env) tv co
= (LC subst' lc_env, tv', co')
where
- (subst', tv', co') = substForAllCoBndrUsing sym sco subst tv co
+ (subst', tv', co') = substForAllCoBndrUsing DCo sym sty sco subst tv co
-- | The \"lifting\" operation which substitutes coercions for type
-- variables in a type to produce a coercion.
@@ -2229,6 +2934,9 @@ liftCoSubstCoVarBndrUsing view_co fun lc@(LC subst cenv) old_var
eta = view_co stuff
k1 = coercionLKind eta
new_var = uniqAway (getSubstInScope subst) (setVarType old_var k1)
+ -- SLD TODO (LC): we should be able to get rid of this call to 'setVarType',
+ -- and thus remove this call to 'coercionLKind' entirely, if we don't store
+ -- the kind of the variable in ForAllCo/ForAllDCo.
-- old_var :: s1 ~r s2
-- eta :: (s1' ~r s2') ~N (t1 ~r t2)
@@ -2338,8 +3046,9 @@ seqCo (FunCo r af1 af2 w co1 co2) = r `seq` af1 `seq` af2 `seq`
seqCo (CoVarCo cv) = cv `seq` ()
seqCo (HoleCo h) = coHoleCoVar h `seq` ()
seqCo (AxiomInstCo con ind cos) = con `seq` ind `seq` seqCos cos
-seqCo (UnivCo p r t1 t2)
- = seqProv p `seq` r `seq` seqType t1 `seq` seqType t2
+seqCo (HydrateDCo r t1 dco rty) = r `seq` seqType t1 `seq` seqDCo dco `seq` seqType rty
+seqCo (UnivCo p r t1 t2) = seqProv seqCo p `seq` r `seq` seqType t1
+ `seq` seqType t2
seqCo (SymCo co) = seqCo co
seqCo (TransCo co1 co2) = seqCo co1 `seq` seqCo co2
seqCo (SelCo n co) = n `seq` seqCo co
@@ -2349,16 +3058,37 @@ seqCo (KindCo co) = seqCo co
seqCo (SubCo co) = seqCo co
seqCo (AxiomRuleCo _ cs) = seqCos cs
-seqProv :: UnivCoProvenance -> ()
-seqProv (PhantomProv co) = seqCo co
-seqProv (ProofIrrelProv co) = seqCo co
-seqProv (PluginProv _) = ()
-seqProv (CorePrepProv _) = ()
+seqDCo :: DCoercion -> ()
+seqDCo ReflDCo = ()
+seqDCo (GReflRightDCo co) = seqCo co
+seqDCo (GReflLeftDCo co) = seqCo co
+seqDCo (TyConAppDCo cos) = seqDCos cos
+seqDCo (AppDCo co1 co2) = seqDCo co1 `seq` seqDCo co2
+seqDCo (ForAllDCo tv k co) = seqType (varType tv) `seq` seqDCo k
+ `seq` seqDCo co
+seqDCo (CoVarDCo cv) = cv `seq` ()
+seqDCo (AxiomInstDCo con) = con `seq` ()
+seqDCo StepsDCo{} = ()
+seqDCo (TransDCo co1 co2) = seqDCo co1 `seq` seqDCo co2
+seqDCo (SubDCo co) = seqDCo co
+seqDCo (DehydrateCo co) = seqCo co
+seqDCo (UnivDCo prov rhs) = seqProv seqDCo prov `seq` seqType rhs
+
+seqProv :: (co -> ()) -> UnivCoProvenance co -> ()
+seqProv seq_co (PhantomProv co) = seq_co co
+seqProv seq_co (ProofIrrelProv co) = seq_co co
+seqProv _ (PluginProv _) = ()
+seqProv _ (CorePrepProv _) = ()
seqCos :: [Coercion] -> ()
seqCos [] = ()
seqCos (co:cos) = seqCo co `seq` seqCos cos
+seqDCos :: [DCoercion] -> ()
+seqDCos [] = ()
+seqDCos (co:cos) = seqDCo co `seq` seqDCos cos
+
+
{-
%************************************************************************
%* *
@@ -2404,6 +3134,7 @@ coercionLKind co
go (CoVarCo cv) = coVarLType cv
go (HoleCo h) = coVarLType (coHoleCoVar h)
go (UnivCo _ _ ty1 _) = ty1
+ go (HydrateDCo _ ty1 _ _) = ty1
go (SymCo co) = coercionRKind co
go (TransCo co1 _) = go co1
go (LRCo lr co) = pickLR lr (splitAppTy (go co))
@@ -2465,6 +3196,7 @@ coercionRKind co
{- See Note [FunCo] -} = FunTy { ft_af = af, ft_mult = go mult
, ft_arg = go arg, ft_res = go res }
go (UnivCo _ _ _ ty2) = ty2
+ go (HydrateDCo _ _ _ rty) = rty
go (SymCo co) = coercionLKind co
go (TransCo _ co2) = go co2
go (LRCo lr co) = pickLR lr (splitAppTy (go co))
@@ -2571,6 +3303,7 @@ coercionRole = go
go (CoVarCo cv) = coVarRole cv
go (HoleCo h) = coVarRole (coHoleCoVar h)
go (AxiomInstCo ax _ _) = coAxiomRole ax
+ go (HydrateDCo r _ _ _) = r
go (UnivCo _ r _ _) = r
go (SymCo co) = go co
go (TransCo co1 _co2) = go co1
@@ -2747,7 +3480,7 @@ buildCoercion orig_ty1 orig_ty2 = go orig_ty1 orig_ty2
has_co_hole_ty :: Type -> Monoid.Any
has_co_hole_co :: Coercion -> Monoid.Any
-(has_co_hole_ty, _, has_co_hole_co, _)
+(has_co_hole_ty, _, has_co_hole_co, _, _, _)
= foldTyCo folder ()
where
folder = TyCoFolder { tcf_view = noView
@@ -2770,7 +3503,7 @@ hasCoercionHoleCo = Monoid.getAny . has_co_hole_co
hasThisCoercionHoleTy :: Type -> CoercionHole -> Bool
hasThisCoercionHoleTy ty hole = Monoid.getAny (f ty)
where
- (f, _, _, _) = foldTyCo folder ()
+ (f, _, _, _, _, _) = foldTyCo folder ()
folder = TyCoFolder { tcf_view = noView
, tcf_tyvar = const2 (Monoid.Any False)
diff --git a/compiler/GHC/Core/Coercion.hs-boot b/compiler/GHC/Core/Coercion.hs-boot
index 276a48cf81..6430e3f6ee 100644
--- a/compiler/GHC/Core/Coercion.hs-boot
+++ b/compiler/GHC/Core/Coercion.hs-boot
@@ -23,7 +23,7 @@ mkFunCo2 :: Role -> FunTyFlag -> FunTyFlag -> CoercionN -> Coercion -> Coerc
mkCoVarCo :: CoVar -> Coercion
mkAxiomInstCo :: CoAxiom Branched -> BranchIndex -> [Coercion] -> Coercion
mkPhantomCo :: Coercion -> Type -> Type -> Coercion
-mkUnivCo :: UnivCoProvenance -> Role -> Type -> Type -> Coercion
+mkUnivCo :: UnivCoProvenance Coercion -> Role -> Type -> Type -> Coercion
mkSymCo :: Coercion -> Coercion
mkTransCo :: Coercion -> Coercion -> Coercion
mkSelCo :: HasDebugCallStack => CoSel -> Coercion -> Coercion
@@ -38,6 +38,20 @@ mkAxiomRuleCo :: CoAxiomRule -> [Coercion] -> Coercion
funRole :: Role -> FunSel -> Role
+mkTyConAppDCo :: [DCoercion] -> DCoercion
+mkAppDCo :: DCoercion -> DCoercion -> DCoercion
+mkTransDCo :: DCoercion -> DCoercion -> DCoercion
+mkForAllDCo :: TyCoVar -> DCoercion -> DCoercion -> DCoercion
+mkReflDCo :: DCoercion
+mkGReflRightDCo :: CoercionN -> DCoercion
+mkGReflLeftDCo :: CoercionN -> DCoercion
+mkDehydrateCo :: Coercion -> DCoercion
+mkHydrateDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> Type -> Coercion
+mkCoVarDCo :: CoVar -> DCoercion
+mkUnivDCo :: UnivCoProvenance DCoercion -> Type -> DCoercion
+mkSubDCo :: HasDebugCallStack => Type -> DCoercion -> Type -> DCoercion
+followDCo :: HasDebugCallStack => Role -> Type -> DCoercion -> Type
+
isGReflCo :: Coercion -> Bool
isReflCo :: Coercion -> Bool
isReflexiveCo :: Coercion -> Bool
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
diff --git a/compiler/GHC/Core/FVs.hs b/compiler/GHC/Core/FVs.hs
index bddd6d89de..3219ef887c 100644
--- a/compiler/GHC/Core/FVs.hs
+++ b/compiler/GHC/Core/FVs.hs
@@ -394,7 +394,8 @@ orphNamesOfCo (FunCo { fco_mult = co_mult, fco_arg = co1, fco_res = co2 })
`unionNameSet` orphNamesOfCo co2
orphNamesOfCo (CoVarCo _) = emptyNameSet
orphNamesOfCo (AxiomInstCo con _ cos) = orphNamesOfCoCon con `unionNameSet` orphNamesOfCos cos
-orphNamesOfCo (UnivCo p _ t1 t2) = orphNamesOfProv p `unionNameSet` orphNamesOfType t1
+orphNamesOfCo (HydrateDCo _ t1 dco _) = orphNamesOfType t1 `unionNameSet` orphNamesOfDCo dco
+orphNamesOfCo (UnivCo p _ t1 t2) = orphNamesOfProv orphNamesOfCo p `unionNameSet` orphNamesOfType t1
`unionNameSet` orphNamesOfType t2
orphNamesOfCo (SymCo co) = orphNamesOfCo co
orphNamesOfCo (TransCo co1 co2) = orphNamesOfCo co1 `unionNameSet` orphNamesOfCo co2
@@ -406,15 +407,33 @@ orphNamesOfCo (SubCo co) = orphNamesOfCo co
orphNamesOfCo (AxiomRuleCo _ cs) = orphNamesOfCos cs
orphNamesOfCo (HoleCo _) = emptyNameSet
-orphNamesOfProv :: UnivCoProvenance -> NameSet
-orphNamesOfProv (PhantomProv co) = orphNamesOfCo co
-orphNamesOfProv (ProofIrrelProv co) = orphNamesOfCo co
-orphNamesOfProv (PluginProv _) = emptyNameSet
-orphNamesOfProv (CorePrepProv _) = emptyNameSet
+orphNamesOfDCo :: DCoercion -> NameSet
+orphNamesOfDCo ReflDCo = emptyNameSet
+orphNamesOfDCo (GReflRightDCo co) = orphNamesOfCo co
+orphNamesOfDCo (GReflLeftDCo co) = orphNamesOfCo co
+orphNamesOfDCo (TyConAppDCo cos) = orphNamesOfDCos cos
+orphNamesOfDCo (AppDCo co1 co2) = orphNamesOfDCo co1 `unionNameSet` orphNamesOfDCo co2
+orphNamesOfDCo (ForAllDCo _ kind_dco co) = orphNamesOfDCo kind_dco `unionNameSet` orphNamesOfDCo co
+orphNamesOfDCo (CoVarDCo _) = emptyNameSet
+orphNamesOfDCo (AxiomInstDCo con) = orphNamesOfCoCon con
+orphNamesOfDCo StepsDCo{} = emptyNameSet
+orphNamesOfDCo (TransDCo co1 co2) = orphNamesOfDCo co1 `unionNameSet` orphNamesOfDCo co2
+orphNamesOfDCo (DehydrateCo co) = orphNamesOfCo co
+orphNamesOfDCo (UnivDCo p rhs) = orphNamesOfProv orphNamesOfDCo p `unionNameSet` orphNamesOfType rhs
+orphNamesOfDCo (SubDCo dco) = orphNamesOfDCo dco
+
+orphNamesOfProv :: (co -> NameSet) -> UnivCoProvenance co -> NameSet
+orphNamesOfProv orph_names (PhantomProv co) = orph_names co
+orphNamesOfProv orph_names (ProofIrrelProv co) = orph_names co
+orphNamesOfProv _ (PluginProv _) = emptyNameSet
+orphNamesOfProv _ (CorePrepProv _) = emptyNameSet
orphNamesOfCos :: [Coercion] -> NameSet
orphNamesOfCos = orphNamesOfThings orphNamesOfCo
+orphNamesOfDCos :: [DCoercion] -> NameSet
+orphNamesOfDCos = orphNamesOfThings orphNamesOfDCo
+
orphNamesOfCoCon :: CoAxiom br -> NameSet
orphNamesOfCoCon (CoAxiom { co_ax_tc = tc, co_ax_branches = branches })
= orphNamesOfTyCon tc `unionNameSet` orphNamesOfCoAxBranches branches
diff --git a/compiler/GHC/Core/FamInstEnv.hs b/compiler/GHC/Core/FamInstEnv.hs
index 0a0389d71b..1d8b05c99d 100644
--- a/compiler/GHC/Core/FamInstEnv.hs
+++ b/compiler/GHC/Core/FamInstEnv.hs
@@ -1,7 +1,6 @@
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TupleSections #-}
-- (c) The University of Glasgow 2006
--
@@ -24,7 +23,7 @@ module GHC.Core.FamInstEnv (
FamInstMatch(..),
lookupFamInstEnv, lookupFamInstEnvConflicts, lookupFamInstEnvByTyCon,
- isDominatedBy, apartnessCheck, compatibleBranches,
+ isDominatedBy, apartnessCheck, compatibleBranches, chooseBranch,
-- Injectivity
InjectivityCheckResult(..),
@@ -33,7 +32,7 @@ module GHC.Core.FamInstEnv (
-- Normalisation
topNormaliseType, topNormaliseType_maybe,
normaliseType, normaliseTcApp,
- topReduceTyFamApp_maybe, reduceTyFamApp_maybe
+ topReduceTyFamApp_maybe, reduceTyFamApp_maybe,
) where
import GHC.Prelude
@@ -1152,18 +1151,17 @@ The lookupFamInstEnv function does a nice job for *open* type families,
but we also need to handle closed ones when normalising a type:
-}
-reduceTyFamApp_maybe :: FamInstEnvs
- -> Role -- Desired role of result coercion
- -> TyCon -> [Type]
- -> Maybe Reduction
--- Attempt to do a *one-step* reduction of a type-family application
+-- | Attempt to do a *one-step* reduction of a type-family application
-- but *not* newtypes
-- Works on type-synonym families always; data-families only if
-- the role we seek is representational
-- It does *not* normalise the type arguments first, so this may not
-- go as far as you want. If you want normalised type arguments,
-- use topReduceTyFamApp_maybe
---
+reduceTyFamApp_maybe :: FamInstEnvs
+ -> Role -- Desired role of result coercion
+ -> TyCon -> [Type]
+ -> Maybe Reduction
-- The TyCon can be oversaturated.
-- Works on both open and closed families
--
@@ -1186,18 +1184,18 @@ reduceTyFamApp_maybe envs role tc tys
-- NB: Allow multiple matches because of compatible overlap
= let co = mkUnbranchedAxInstCo role ax inst_tys inst_cos
- in Just $ coercionRedn co
+ in Just $ mkDehydrateCoercionRedn co
| Just ax <- isClosedSynFamilyTyConWithAxiom_maybe tc
, Just (ind, inst_tys, inst_cos) <- chooseBranch ax tys
= let co = mkAxInstCo role ax ind inst_tys inst_cos
- in Just $ coercionRedn co
+ in Just $ mkDehydrateCoercionRedn co
- | Just ax <- isBuiltInSynFamTyCon_maybe tc
- , Just (coax,ts,ty) <- sfMatchFam ax tys
+ | Just ax <- isBuiltInSynFamTyCon_maybe tc
+ , Just (coax,ts,_) <- sfMatchFam ax tys
, role == coaxrRole coax
= let co = mkAxiomRuleCo coax (zipWith mkReflCo (coaxrAsmpRoles coax) ts)
- in Just $ mkReduction co ty
+ in Just $ mkDehydrateCoercionRedn co
| otherwise
= Nothing
@@ -1351,21 +1349,20 @@ topNormaliseType_maybe :: FamInstEnvs -> Type -> Maybe Reduction
-- Always operates homogeneously: the returned type has the same kind as the
-- original type, and the returned coercion is always homogeneous.
topNormaliseType_maybe env ty
- = do { ((co, mkind_co), nty) <- topNormaliseTypeX stepper combine ty
- ; let hredn = mkHetReduction (mkReduction co nty) mkind_co
- ; return $ homogeniseHetRedn Representational hredn }
+ = do { ((dco, mkind_co), nty) <- topNormaliseTypeX stepper combine ty
+ ; return $ homogeniseRedn (mkReduction dco nty) mkind_co }
where
stepper = unwrapNewTypeStepper' `composeSteppers` tyFamStepper
- combine (c1, mc1) (c2, mc2) = (c1 `mkTransCo` c2, mc1 `mkTransMCo` mc2)
+ combine (c1, mdc1) (c2, mdc2) = (c1 `mkTransDCo` c2, mdc1 `mkTransMCo` mdc2)
- unwrapNewTypeStepper' :: NormaliseStepper (Coercion, MCoercionN)
+ unwrapNewTypeStepper' :: NormaliseStepper (DCoercion, MCoercionN)
unwrapNewTypeStepper' rec_nts tc tys
- = (, MRefl) <$> unwrapNewTypeStepper rec_nts tc tys
+ = (\ co -> (mkDehydrateCo co, MRefl)) <$> unwrapNewTypeStepper rec_nts tc tys
-- second coercion below is the kind coercion relating the original type's kind
-- to the normalised type's kind
- tyFamStepper :: NormaliseStepper (Coercion, MCoercionN)
+ tyFamStepper :: NormaliseStepper (DCoercion, MCoercionN)
tyFamStepper rec_nts tc tys -- Try to step a type/data family
= case topReduceTyFamApp_maybe env tc tys of
Just (HetReduction (Reduction co rhs) res_co)
@@ -1385,13 +1382,13 @@ topReduceTyFamApp_maybe envs fam_tc arg_tys
, Just redn <- reduceTyFamApp_maybe envs role fam_tc ntys
= Just $
mkHetReduction
- (mkTyConAppCo role fam_tc args_cos `mkTransRedn` redn)
+ (mkTyConAppRedn fam_tc args_redns `mkTransRedn` redn)
res_co
| otherwise
= Nothing
where
role = Representational
- ArgsReductions (Reductions args_cos ntys) res_co
+ ArgsReductions args_redns@(Reductions _ ntys) res_co
= initNormM envs role (tyCoVarsOfTypes arg_tys)
$ normalise_tc_args fam_tc arg_tys
@@ -1427,16 +1424,16 @@ normalise_tc_app tc tys
= -- A type-family application
do { env <- getEnv
; role <- getRole
- ; ArgsReductions redns@(Reductions args_cos ntys) res_co <- normalise_tc_args tc tys
+ ; ArgsReductions redns@(Reductions _ ntys) res_co <- normalise_tc_args tc tys
; case reduceTyFamApp_maybe env role tc ntys of
Just redn1
-> do { redn2 <- normalise_reduction redn1
- ; let redn3 = mkTyConAppCo role tc args_cos `mkTransRedn` redn2
- ; return $ assemble_result role redn3 res_co }
+ ; let redn3 = mkTyConAppRedn tc redns `mkTransRedn` redn2
+ ; return $ homogeniseRedn redn3 res_co }
_ -> -- No unique matching family instance exists;
-- we do not do anything
return $
- assemble_result role (mkTyConAppRedn role tc redns) res_co }
+ homogeniseRedn (mkTyConAppRedn tc redns) res_co }
| otherwise
= -- A synonym with no type families in the RHS; or data type etc
@@ -1444,16 +1441,10 @@ normalise_tc_app tc tys
do { ArgsReductions redns res_co <- normalise_tc_args tc tys
; role <- getRole
; return $
- assemble_result role (mkTyConAppRedn role tc redns) res_co }
-
- where
- assemble_result :: Role -- r, ambient role in NormM monad
- -> Reduction -- orig_ty ~r nty, possibly heterogeneous (nty possibly of changed kind)
- -> MCoercionN -- typeKind(orig_ty) ~N typeKind(nty)
- -> Reduction -- orig_ty ~r nty_casted
- -- where nty_casted has same kind as orig_ty
- assemble_result r redn kind_co
- = mkCoherenceRightMRedn r redn (mkSymMCo kind_co)
+ homogeniseRedn (mkTyConAppRedn_MightBeSynonym role tc tys redns) res_co }
+ -- NB: we assume "tys" satisfy the hydration invariant from
+ -- Note [Following a directed coercion] in GHC.Core.Coercion,
+ -- because the "normalise" functions all only deal with fully zonked types.
normalise_tc_args :: TyCon -> [Type] -> NormM ArgsReductions
normalise_tc_args tc tys
@@ -1475,16 +1466,17 @@ normalise_type ty
go :: Type -> NormM Reduction
go (TyConApp tc tys) = normalise_tc_app tc tys
go ty@(LitTy {})
- = do { r <- getRole
- ; return $ mkReflRedn r ty }
+ = return $ mkReflRedn ty
go (AppTy ty1 ty2) = go_app_tys ty1 [ty2]
go (FunTy { ft_af = vis, ft_mult = w, ft_arg = ty1, ft_res = ty2 })
= do { arg_redn <- go ty1
; res_redn <- go ty2
; w_redn <- withRole Nominal $ go w
- ; r <- getRole
- ; return $ mkFunRedn r vis w_redn arg_redn res_redn }
+ ; return $ mkFunRedn vis w_redn mkReflDCo mkReflDCo arg_redn res_redn
+ -- NB: normalise_type is homogeneous, so we can use ReflDCo
+ -- for the kind coercions.
+ }
go (ForAllTy (Bndr tcvar vis) ty)
= do { (lc', tv', k_redn) <- normalise_var_bndr tcvar
; redn <- withLC lc' $ normalise_type ty
@@ -1494,15 +1486,14 @@ normalise_type ty
= do { redn <- go ty
; lc <- getLC
; let co' = substRightCo lc co
- ; return $ mkCastRedn2 Nominal ty co redn co'
+ ; return $ mkCastRedn2 co redn co'
-- ^^^^^^^^^^^ uses castCoercionKind2
}
go (CoercionTy co)
= do { lc <- getLC
- ; r <- getRole
; let kco = liftCoSubst Nominal lc (coercionType co)
co' = substRightCo lc co
- ; return $ mkProofIrrelRedn r kco co co' }
+ ; return $ mkProofIrrelRedn co (mkDehydrateCo kco) co' }
go_app_tys :: Type -- function
-> [Type] -- args
@@ -1510,7 +1501,7 @@ normalise_type ty
-- cf. GHC.Tc.Solver.Rewrite.rewrite_app_ty_args
go_app_tys (AppTy ty1 ty2) tys = go_app_tys ty1 (ty2 : tys)
go_app_tys fun_ty arg_tys
- = do { fun_redn@(Reduction fun_co nfun) <- go fun_ty
+ = do { fun_redn@(Reduction _ nfun) <- go fun_ty
; case tcSplitTyConApp_maybe nfun of
Just (tc, xis) ->
do { redn <- go (mkTyConApp tc (xis ++ arg_tys))
@@ -1518,15 +1509,14 @@ normalise_type ty
-- but that's a much more performance-sensitive function.
-- This type normalisation is not called in a loop.
; return $
- mkAppCos fun_co (map mkNomReflCo arg_tys) `mkTransRedn` redn }
+ mkAppRedns fun_redn (mkReflRedns arg_tys) `mkTransRedn` redn }
Nothing ->
do { ArgsReductions redns res_co
<- normalise_args (typeKind nfun)
(Inf.repeat Nominal)
arg_tys
- ; role <- getRole
; return $
- mkCoherenceRightMRedn role
+ mkCoherenceRightMRedn
(mkAppRedns fun_redn redns)
(mkSymMCo res_co) } }
@@ -1542,7 +1532,7 @@ normalise_args :: Kind -- of the function
-- cf. GHC.Tc.Solver.Rewrite.rewrite_args_slow
normalise_args fun_ki roles args
= do { normed_args <- zipWithM normalise1 (Inf.toList roles) args
- ; return $ simplifyArgsWorker ki_binders inner_ki fvs roles normed_args }
+ ; return $ simplifyArgsWorker ki_binders inner_ki fvs roles args normed_args }
where
(ki_binders, inner_ki) = splitPiTys fun_ki
fvs = tyCoVarsOfTypes args
@@ -1556,21 +1546,25 @@ normalise_tyvar tv
do { lc <- getLC
; r <- getRole
; return $ case liftCoSubstTyVar lc r tv of
- Just co -> coercionRedn co
- Nothing -> mkReflRedn r (mkTyVarTy tv) }
+ Just co -> mkDehydrateCoercionRedn co
+ Nothing -> mkReflRedn (mkTyVarTy tv) }
normalise_reduction :: Reduction -> NormM Reduction
-normalise_reduction (Reduction co ty)
+normalise_reduction redn@(Reduction _ ty)
= do { redn' <- normalise_type ty
- ; return $ co `mkTransRedn` redn' }
+ ; return $ redn `mkTransRedn` redn' }
normalise_var_bndr :: TyCoVar -> NormM (LiftingContext, TyCoVar, Reduction)
normalise_var_bndr tcvar
-- works for both tvar and covar
= do { lc1 <- getLC
; env <- getEnv
- ; let callback lc ki = runNormM (normalise_type ki) env lc Nominal
- ; return $ liftCoSubstVarBndrUsing reductionCoercion callback lc1 tcvar }
+ ; let
+ mk_co (lhs, redn) = mkHydrateReductionDCoercion Nominal lhs redn
+ do_normalise ki = do { redn <- normalise_type ki; return (ki, redn) }
+ callback lc ki = runNormM (do_normalise ki) env lc Nominal
+ (lc, tcv, (_, redn)) = liftCoSubstVarBndrUsing mk_co callback lc1 tcvar
+ ; return (lc, tcv, redn) }
-- | a monad for the normalisation functions, reading 'FamInstEnvs',
-- a 'LiftingContext', and a 'Role'.
diff --git a/compiler/GHC/Core/FamInstEnv.hs-boot b/compiler/GHC/Core/FamInstEnv.hs-boot
new file mode 100644
index 0000000000..6679d4bae7
--- /dev/null
+++ b/compiler/GHC/Core/FamInstEnv.hs-boot
@@ -0,0 +1,9 @@
+module GHC.Core.FamInstEnv where
+
+import GHC.Core.Coercion.Axiom (CoAxiom, BranchIndex, Branched)
+import GHC.Core.TyCo.Rep (Coercion)
+import GHC.Core.Type (Type)
+import Data.Maybe (Maybe)
+
+chooseBranch :: CoAxiom Branched -> [Type]
+ -> Maybe (BranchIndex, [Type], [Coercion])
diff --git a/compiler/GHC/Core/Lint.hs b/compiler/GHC/Core/Lint.hs
index 7bb1eb43aa..449774299b 100644
--- a/compiler/GHC/Core/Lint.hs
+++ b/compiler/GHC/Core/Lint.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE PatternSynonyms #-}
@@ -1751,11 +1753,20 @@ lintValueType ty
2 (text "has kind:" <+> ppr sk)
; return ty' }
+
+-------------------
checkTyCon :: TyCon -> LintM ()
checkTyCon tc
= checkL (not (isTcTyCon tc)) (text "Found TcTyCon:" <+> ppr tc)
-------------------
+checkTyCoVarInScope :: String -> Subst -> TyCoVar -> LintM ()
+checkTyCoVarInScope what subst tcv
+ = checkL (tcv `isInScope` subst) $
+ hang (text "The" <+> text what <+> text "variable" <+> pprBndr LetBind tcv)
+ 2 (text "is out of scope")
+
+-------------------
lintType :: Type -> LintM LintedType
-- If you edit this function, you may need to update the GHC formalism
@@ -1772,12 +1783,9 @@ lintType (TyVarTy tv)
-- In GHCi we may lint an expression with a free
-- type variable. Then it won't be in the
-- substitution, but it should be in scope
- Nothing | tv `isInScope` subst
- -> return (TyVarTy tv)
- | otherwise
- -> failWithL $
- hang (text "The type variable" <+> pprBndr LetBind tv)
- 2 (text "is out of scope")
+ Nothing ->
+ do { checkTyCoVarInScope "type" subst tv
+ ; return (TyVarTy tv) }
}
lintType ty@(AppTy t1 t2)
@@ -2125,18 +2133,6 @@ which is what used to happen. But that proved tricky and error prone
(#17923), so now we return the coercion.
-}
-
--- lints a coercion, confirming that its lh kind and its rh kind are both *
--- also ensures that the role is Nominal
-lintStarCoercion :: InCoercion -> LintM LintedCoercion
-lintStarCoercion g
- = do { g' <- lintCoercion g
- ; let Pair t1 t2 = coercionKind g'
- ; checkValueType t1 (text "the kind of the left type in" <+> ppr g)
- ; checkValueType t2 (text "the kind of the right type in" <+> ppr g)
- ; lintRole g Nominal (coercionRole g)
- ; return g' }
-
lintCoercion :: InCoercion -> LintM LintedCoercion
-- If you edit this function, you may need to update the GHC formalism
-- See Note [GHC Formalism]
@@ -2150,18 +2146,12 @@ lintCoercion (CoVarCo cv)
= do { subst <- getSubst
; case lookupCoVar subst cv of
Just linted_co -> return linted_co ;
- Nothing
- | cv `isInScope` subst
- -> return (CoVarCo cv)
- | otherwise
- ->
- -- lintCoBndr always extends the substitution
- failWithL $
- hang (text "The coercion variable" <+> pprBndr LetBind cv)
- 2 (text "is out of scope")
+ Nothing ->
+ -- lintCoBndr always extends the substitition
+ do { checkTyCoVarInScope "coercion" subst cv
+ ; return (CoVarCo cv) }
}
-
lintCoercion (Refl ty)
= do { ty' <- lintType ty
; return (Refl ty') }
@@ -2229,7 +2219,13 @@ lintCoercion co@(ForAllCo tcv kind_co body_co)
; lintTyCoBndr tcv $ \tcv' ->
do { body_co' <- lintCoercion body_co
; ensureEqTys (varType tcv') (coercionLKind kind_co') $
- text "Kind mis-match in ForallCo" <+> ppr co
+ vcat [ text "Kind mis-match in ForallCo" <+> ppr co
+ , text "Type variable type:" <+> ppr (varType tcv')
+ , text " Coercion LHS type:" <+> ppr (coercionLKind kind_co')
+ , text "Type variable:" <+> ppr tcv
+ , text " Linted tyvar:" <+> ppr tcv'
+ , text "Kind coercion:" <+> ppr kind_co
+ , text " Linted kco:" <+> ppr kind_co ]
-- Assuming kind_co :: k1 ~ k2
-- Need to check that
@@ -2278,13 +2274,27 @@ lintCoercion co@(FunCo { fco_role = r, fco_afl = afl, fco_afr = afr
, text "arg_co:" <+> ppr co1
, text "res_co:" <+> ppr co2 ])
+lintCoercion (HydrateDCo r ty dco rty) =
+ do { ty' <- lintType ty
+ ; rty' <- lintType rty
+ ; co <- lintDCoercion r ty' dco
+ ; let rty = coercionRKind co
+ ; ensureEqTys rty' rty $
+ vcat [ text "Mismatch of cached RHS type in HydrateDCo"
+ , text "dco:" <+> ppr dco
+ , text "stored RHS:" <+> ppr rty'
+ , text "computed RHS:" <+> ppr rty
+ , text "LHS:" <+> ppr ty
+ , text "role:" <+> ppr r ]
+ ; return co }
+
-- See Note [Bad unsafe coercion]
-lintCoercion co@(UnivCo prov r ty1 ty2)
+lintCoercion (UnivCo prov r ty1 ty2)
= do { ty1' <- lintType ty1
; ty2' <- lintType ty2
; let k1 = typeKind ty1'
k2 = typeKind ty2'
- ; prov' <- lint_prov k1 k2 prov
+ ; prov' <- lintProv Co r ty1' ty2' prov
; when (r /= Phantom && isTYPEorCONSTRAINT k1
&& isTYPEorCONSTRAINT k2)
@@ -2343,27 +2353,6 @@ lintCoercion co@(UnivCo prov r ty1 ty2)
_ -> return ()
}
- lint_prov k1 k2 (PhantomProv kco)
- = do { kco' <- lintStarCoercion kco
- ; lintRole co Phantom r
- ; check_kinds kco' k1 k2
- ; return (PhantomProv kco') }
-
- lint_prov k1 k2 (ProofIrrelProv kco)
- = do { lintL (isCoercionTy ty1) (mkBadProofIrrelMsg ty1 co)
- ; lintL (isCoercionTy ty2) (mkBadProofIrrelMsg ty2 co)
- ; kco' <- lintStarCoercion kco
- ; check_kinds kco k1 k2
- ; return (ProofIrrelProv kco') }
-
- lint_prov _ _ prov@(PluginProv _) = return prov
- lint_prov _ _ prov@(CorePrepProv _) = return prov
-
- check_kinds kco k1 k2
- = do { let Pair k1' k2' = coercionKind kco
- ; ensureEqTys k1 k1' (mkBadUnivCoMsg CLeft co)
- ; ensureEqTys k2 k2' (mkBadUnivCoMsg CRight co) }
-
lintCoercion (SymCo co)
= do { co' <- lintCoercion co
@@ -2530,6 +2519,206 @@ lintCoercion (HoleCo h)
= do { addErrL $ text "Unfilled coercion hole:" <+> ppr h
; lintCoercion (CoVarCo (coHoleCoVar h)) }
+lintDCoercion :: Role -> LintedType -> DCoercion -> LintM LintedCoercion
+lintDCoercion r l_ty dco = case dco of
+
+ CoVarDCo cv
+ | not (isCoVar cv)
+ -> failWithL (hang (text "Bad CoVarDCo:" <+> ppr cv)
+ 2 (text "With offending type:" <+> ppr (varType cv)))
+ | otherwise
+ -> do { lintRole dco r (coVarRole cv)
+ ; cv_ty_l <- lintType $ coVarLType cv
+ ; ensureEqTys l_ty cv_ty_l
+ (hang (text "lintDCoercion: CoVarDCo LHS mis-match:" <+> ppr cv)
+ 2 (vcat [text "Expected type:" <+> ppr l_ty
+ ,text " Actual type:" <+> ppr cv_ty_l]))
+ ; subst <- getSubst
+ ; case lookupCoVar subst cv of
+ Just linted_co
+ -> return linted_co
+ Nothing
+ | cv `isInScope` subst
+ -> return (CoVarCo cv)
+ | otherwise
+ -> -- lintCoBndr always extends the substitition
+ failWithL $
+ hang (text "The coercion variable" <+> pprBndr LetBind cv)
+ 2 (text "is out of scope in this directed coercion")
+ }
+
+ ReflDCo ->
+ -- N.B.: the role might well not be Nominal.
+ return (mkReflCo r l_ty)
+
+ GReflRightDCo co ->
+ do { co' <- lintCoercion co
+ ; return (GRefl r l_ty (coToMCo co')) }
+
+ GReflLeftDCo co ->
+ do { sym_co' <- lintCoercion (mkSymCo co)
+ ; return (GRefl r l_ty (coToMCo sym_co')) }
+
+ SubDCo dco ->
+ do { lintRole dco Representational r
+ ; co <- lintDCoercion Nominal l_ty dco
+ ; return (SubCo co) }
+
+ TyConAppDCo dcos
+ | Just (tc, l_tys) <- splitTyConApp_maybe l_ty
+ -> do { checkTyCon tc
+ ; checkL (dcos `equalLength` l_tys) $
+ vcat [ text "mismatched number of arguments in TyConAppDCo"
+ , text "args:" <+> ppr l_tys
+ , text "dcos:" <+> ppr dcos ]
+ ; cos <- zipWith3M lintDCoercion (tyConRoleListX r tc) l_tys dcos
+ ; lintCoercion $ mkTyConAppCo r tc cos }
+ | otherwise
+ -> failWithL (text "TyConAppDCo where LHS type is not a TyCon:" <+> ppr l_ty)
+
+ AppDCo dco1 dco2
+{- | TyConAppDCo {} <- dco1
+ -> failWithL (text "TyConAppDCo to the left of AppDCo:" <+> ppr dco)
+ | TyConApp {} <- l_ty
+ , isReflDCo dco1
+ -> failWithL (text "ReflDCo (TyConApp ...) to the left of AppDCo:" <+> ppr dco)
+-}
+ | Just (l_ty1, l_ty2) <- splitAppTy_maybe l_ty
+ -> do { co1' <- lintDCoercion r l_ty1 dco1
+ ; let
+ r2
+ | Phantom <- r
+ = Phantom
+ | otherwise
+ = Nominal
+ ; co2' <- lintDCoercion r2 l_ty2 dco2
+ ; return (mkAppCo co1' co2') }
+ | otherwise
+ -> failWithL (text "AppDCo where type is not an AppTy:" <+> ppr l_ty)
+
+ ForAllDCo tcv kco body_dco
+ | not (isTyCoVar tcv)
+ -> failWithL (text "Non tyco binder in ForAllDCo:" <+> ppr dco)
+ | otherwise
+ -> do { let l_ki = tyVarKind tcv
+ ; kco' <- lintStarDCoercion Nominal l_ki kco
+ ; lintTyCoBndr tcv $ \tcv' ->
+ case splitForAllTyCoVar_maybe l_ty of
+ Nothing -> failWithL (text "ForAllDCo where LHS type is not a ForAllTy: " <+> ppr l_ty <+> ppr dco)
+ Just (tcv'', body_ty) ->
+ do { in_scope <- getInScope
+ -- AMG TODO: is there a cleaner way of doing this?
+ ; let body_ty' = substTyWithInScope in_scope [tcv''] [mkTyVarTy tcv'] body_ty
+ ; lintForAllBody tcv' body_ty'
+ ; body_co <- lintDCoercion r body_ty' body_dco
+ ; let rhs_ty = coercionRKind body_co
+ ; lintForAllBody tcv' rhs_ty -- AMG TODO: check anything else about rhs_ty?
+ ; let co' = ForAllCo tcv' kco' body_co
+ -- AMG TODO: if CoVar, check occurs only in Refl/GRefl?
+ ; pure co'
+ } }
+
+ AxiomInstDCo ax ->
+ do { (co, _ty) <- expandAxiomInstDCo (\ prop msg v -> lintL prop msg *> v) failWithL
+ r l_ty ax
+ ; return co }
+
+ StepsDCo 0
+ -> return (mkReflCo r l_ty)
+ StepsDCo n ->
+ do { (co, ty) <- expandOneStepDCo (\ prop msg v -> lintL prop msg *> v) failWithL
+ r l_ty
+ ; co' <- lintDCoercion r ty (StepsDCo (n-1))
+ ; lintCoercion $ co `mkTransCo` co' }
+
+ UnivDCo prov r_ty ->
+ do { r_ty' <- lintType r_ty
+ ; prov' <- lintProv DCo r l_ty r_ty prov
+ ; return (UnivCo prov' r l_ty r_ty') }
+
+ TransDCo dco1 dco2 ->
+ do { co1' <- lintDCoercion r l_ty dco1
+ ; let mid_ty = coercionRKind co1'
+ ; co2' <- lintDCoercion r mid_ty dco2
+ ; return (TransCo co1' co2') }
+
+ DehydrateCo co ->
+ do { co' <- lintCoercion co
+ ; let co_ty_l = coercionLKind co'
+ co_r = coercionRole co'
+
+ ; checkL (r == co_r) $
+ hang (text "lintDCoercion: DehydrateCo role mismatch:" <+> ppr co)
+ 2 (vcat [text "Expected role:" <+> ppr r
+ ,text " Actual role:" <+> ppr co_r])
+ ; ensureEqTys l_ty co_ty_l
+ (hang (text "lintDCoercion: DehydrateCo LHS mis-match:" <+> ppr co)
+ 2 (vcat [text "Expected type:" <+> ppr l_ty
+ ,text " Actual type:" <+> ppr co_ty_l]))
+ ; return co' }
+
+-- | Lint the provenance of a universal coercion (or directed coercion).
+lintProv :: forall co
+ . CoOrDCo co
+ -> Role
+ -> LintedType
+ -> LintedType
+ -> UnivCoProvenance co
+ -> LintM (UnivCoProvenance LintedCoercion)
+lintProv co_or_dco r ty1 ty2 prov = case prov of
+ PhantomProv kco ->
+ do { kco' <- lint_star kco
+ ; lintRole prov Phantom r
+ ; check_kinds kco'
+ ; return (PhantomProv kco') }
+
+ ProofIrrelProv kco ->
+ do { lintL (isCoercionTy ty1) (mkBadProofIrrelMsg ty1 prov)
+ ; lintL (isCoercionTy ty2) (mkBadProofIrrelMsg ty2 prov)
+ ; kco' <- lint_star kco
+ ; check_kinds kco'
+ ; return (ProofIrrelProv kco') }
+
+ PluginProv str ->
+ return $ PluginProv str
+ CorePrepProv homo ->
+ return $ CorePrepProv homo
+
+ where
+ k1, k2 :: LintedKind
+ k1 = typeKind ty1
+ k2 = typeKind ty2
+
+ lint_star :: co -> LintM LintedCoercion
+ lint_star
+ = case co_or_dco of
+ Co -> lintStarCoercion
+ DCo -> lintStarDCoercion r k1
+
+ check_kinds :: Coercion -> LintM ()
+ check_kinds kco
+ = do { let Pair k1' k2' = coercionKind kco
+ ; ensureEqTys k1 k1' (mkBadUnivCoMsg CLeft prov)
+ ; ensureEqTys k2 k2' (mkBadUnivCoMsg CRight prov) }
+
+-- | Lints a kind coercion, confirming that its LHS kind and its RHS kind are both
+-- @Type@, and ensures that its role is 'Nominal'.
+lintStarCoercion :: InCoercion -> LintM LintedCoercion
+lintStarCoercion g = checkStarCoercion (coercionRole g) =<< lintCoercion g
+
+-- | Like 'lintStarCoercion' but for a directed coercion.
+lintStarDCoercion :: Role -> LintedKind -> DCoercion -> LintM LintedCoercion
+lintStarDCoercion r l_ki g = checkStarCoercion r =<< lintDCoercion Nominal l_ki g
+
+-- | Performs the checks required by 'lintStarCoercion'/'lintStarDCoercion',
+-- after the kind coercion has been linted on its own.
+checkStarCoercion :: Role -> Coercion -> LintM LintedCoercion
+checkStarCoercion r g
+ = do { lintRole g Nominal r
+ ; let Pair k1 k2 = coercionKind g
+ ; checkValueType k1 (text "the kind of the left type in" <+> ppr g)
+ ; checkValueType k2 (text "the kind of the right type in" <+> ppr g)
+ ; return g }
{-
Note [Conflict checking with AxiomInstCo]
@@ -3313,13 +3502,14 @@ ensureSubMult actual_mult described_mult err_msg = do
| otherwise = m `eqType` n
lintRole :: Outputable thing
- => thing -- where the role appeared
- -> Role -- expected
- -> Role -- actual
- -> LintM ()
+ => thing -- where the role appeared
+ -> Role -- expected
+ -> Role -- actual
+ -> LintM ()
lintRole co r1 r2
= lintL (r1 == r2)
(text "Role incompatibility: expected" <+> ppr r1 <> comma <+>
+ text ": expected" <+> ppr r1 <> comma <+>
text "got" <+> ppr r2 $$
text "in" <+> ppr co)
@@ -3550,16 +3740,16 @@ mk_cast_err thing_str co_str pp_thing co from_ty thing_ty
from_msg = text "From-" <> co_msg
enclosed_msg = text "enclosed" <+> text thing_str
-mkBadUnivCoMsg :: LeftOrRight -> Coercion -> SDoc
-mkBadUnivCoMsg lr co
+mkBadUnivCoMsg :: LeftOrRight -> UnivCoProvenance co -> SDoc
+mkBadUnivCoMsg lr prov
= text "Kind mismatch on the" <+> pprLeftOrRight lr <+>
- text "side of a UnivCo:" <+> ppr co
+ text "side of a UnivCo:" <+> ppr prov
-mkBadProofIrrelMsg :: Type -> Coercion -> SDoc
-mkBadProofIrrelMsg ty co
+mkBadProofIrrelMsg :: Outputable prov => Type -> prov -> SDoc
+mkBadProofIrrelMsg ty prov
= hang (text "Found a non-coercion in a proof-irrelevance UnivCo:")
2 (vcat [ text "type:" <+> ppr ty
- , text "co:" <+> ppr co ])
+ , text "prov:" <+> ppr prov ])
mkBadTyVarMsg :: Var -> SDoc
mkBadTyVarMsg tv
diff --git a/compiler/GHC/Core/Opt/Pipeline.hs b/compiler/GHC/Core/Opt/Pipeline.hs
index 0761691f84..7e626bb374 100644
--- a/compiler/GHC/Core/Opt/Pipeline.hs
+++ b/compiler/GHC/Core/Opt/Pipeline.hs
@@ -199,7 +199,7 @@ getCoreToDo dflags hpt_rule_base extra_vars
runWhen (profiling && gopt Opt_ProfLateInlineCcs dflags) $ CoreAddLateCcs
core_todo =
- [
+ [
-- We want to do the static argument transform before full laziness as it
-- may expose extra opportunities to float things outwards. However, to fix
-- up the output of the transformation we need at do at least one simplify
diff --git a/compiler/GHC/Core/Opt/Simplify/Iteration.hs b/compiler/GHC/Core/Opt/Simplify/Iteration.hs
index 1ecfa632e1..794b33b1fa 100644
--- a/compiler/GHC/Core/Opt/Simplify/Iteration.hs
+++ b/compiler/GHC/Core/Opt/Simplify/Iteration.hs
@@ -3254,9 +3254,11 @@ improveSeq :: (FamInstEnv, FamInstEnv) -> SimplEnv
-> SimplM (SimplEnv, OutExpr, OutId)
-- Note [Improving seq]
improveSeq fam_envs env scrut case_bndr case_bndr1 [Alt DEFAULT _ _]
- | Just (Reduction co ty2) <- topNormaliseType_maybe fam_envs (idType case_bndr1)
+ | let ty1 = idType case_bndr1
+ , Just redn@(Reduction _ ty2) <- topNormaliseType_maybe fam_envs ty1
= do { case_bndr2 <- newId (fsLit "nt") ManyTy ty2
- ; let rhs = DoneEx (Var case_bndr2 `Cast` mkSymCo co) Nothing
+ ; let co = mkHydrateReductionDCoercion Representational ty1 redn
+ rhs = DoneEx (Var case_bndr2 `Cast` mkSymCo co) Nothing
env2 = extendIdSubst env case_bndr rhs
; return (env2, scrut `Cast` co, case_bndr2) }
diff --git a/compiler/GHC/Core/Reduction.hs b/compiler/GHC/Core/Reduction.hs
index a4f1df4f70..bfa2b09523 100644
--- a/compiler/GHC/Core/Reduction.hs
+++ b/compiler/GHC/Core/Reduction.hs
@@ -7,18 +7,21 @@ module GHC.Core.Reduction
-- * Reductions
Reduction(..), ReductionN, ReductionR, HetReduction(..),
Reductions(..),
- mkReduction, mkReductions, mkHetReduction, coercionRedn,
- reductionOriginalType,
- downgradeRedn, mkSubRedn,
+ mkReduction, mkReductions, mkHetReduction, mkDehydrateCoercionRedn,
+ mkHydrateReductionDCoercion,
+ mkSubRedn,
mkTransRedn, mkCoherenceRightRedn, mkCoherenceRightMRedn,
mkCastRedn1, mkCastRedn2,
mkReflRedn, mkGReflRightRedn, mkGReflRightMRedn,
mkGReflLeftRedn, mkGReflLeftMRedn,
mkAppRedn, mkAppRedns, mkFunRedn,
mkForAllRedn, mkHomoForAllRedn, mkTyConAppRedn, mkClassPredRedn,
+ mkTyConAppRedn_MightBeSynonym,
mkProofIrrelRedn, mkReflCoRedn,
- homogeniseHetRedn,
+ homogeniseHetRedn, homogeniseRedn,
unzipRedns,
+ mkReflRedns,
+ mkReflDCos,
-- * Rewriting type arguments
ArgsReductions(..),
@@ -28,15 +31,14 @@ module GHC.Core.Reduction
import GHC.Prelude
-import GHC.Core.Class ( Class(classTyCon) )
+import GHC.Core.Class ( Class(..) )
import GHC.Core.Coercion
import GHC.Core.Predicate ( mkClassPred )
-import GHC.Core.TyCon ( TyCon )
+import GHC.Core.TyCon
import GHC.Core.Type
import GHC.Data.Pair ( Pair(Pair) )
import GHC.Data.List.Infinite ( Infinite (..) )
-import qualified GHC.Data.List.Infinite as Inf
import GHC.Types.Var ( VarBndr(..), setTyVarKind )
import GHC.Types.Var.Env ( mkInScopeSet )
@@ -46,6 +48,8 @@ import GHC.Utils.Misc ( HasDebugCallStack, equalLength )
import GHC.Utils.Outputable
import GHC.Utils.Panic ( assertPpr )
+import Data.List ( zipWith4 )
+
{-
%************************************************************************
%* *
@@ -55,170 +59,119 @@ import GHC.Utils.Panic ( assertPpr )
Note [The Reduction type]
~~~~~~~~~~~~~~~~~~~~~~~~~
-Many functions in the type-checker rewrite a type, using Given type equalitie
-or type-family reductions, and return a Reduction, which is just a pair of the
-coercion and the RHS type of the coercion:
- data Reduction = Reduction Coercion !Type
+Many functions in the type-checker rewrite a type, using Given type equalities
+or type-family reductions, and return a Reduction:
+
+ data Reduction = Reduction DCoercion !Type
+
+When we rewrite ty at role r, producing Reduction dco xi, we guarantee that
+dco :: ty ~r xi, up to zonking.
+
+In particular, if ty is fully zonked, we can call followDCo, and we should have
+
+ followDCo r ty dco ~ xi (up to zonking)
-The order of the arguments to the constructor serves as a reminder
-of what the Type is. In
- Reduction co ty
-`ty` appears to the right of `co`, reminding us that we must have:
- co :: unrewritten_ty ~ ty
+The order of the arguments to the constructor serves as a reminder:
+
+ ty ~rewrite~> Reduction dco xi
+
+the rewritten type appears on the right, reminding us that we must have:
+
+ dco :: ty ~r xi
Example functions that use this datatype:
+
GHC.Core.FamInstEnv.topNormaliseType_maybe
:: FamInstEnvs -> Type -> Maybe Reduction
GHC.Tc.Solver.Rewrite.rewrite
:: CtEvidence -> TcType -> TcS Reduction
Having Reduction as a data type, with a strict Type field, rather than using
-a pair (Coercion,Type) gives several advantages (see #20161)
+a tuple (with all fields lazy), gives several advantages (see #20161)
* The strictness in Type improved performance in rewriting of type families
(around 2.5% improvement in T9872),
* Compared to the situation before, it gives improved consistency around
orientation of rewritings, as a Reduction is always left-to-right
(the coercion's RHS type is always the type stored in the 'Reduction').
No more 'mkSymCo's needed to convert between left-to-right and right-to-left.
-
-One could imagine storing the LHS type of the coercion in the Reduction as well,
-but in fact `reductionOriginalType` is very seldom used, so it's not worth it.
-}
-- | A 'Reduction' is the result of an operation that rewrites a type @ty_in@.
--- The 'Reduction' includes the rewritten type @ty_out@ and a 'Coercion' @co@
--- such that @co :: ty_in ~ ty_out@, where the role of the coercion is determined
--- by the context. That is, the LHS type of the coercion is the original type
--- @ty_in@, while its RHS type is the rewritten type @ty_out@.
+-- The 'Reduction' includes:
+--
+-- - a directed coercion @dco@,
+-- - the rewritten type @ty_out@
--
--- A Reduction is always homogeneous, unless it is wrapped inside a 'HetReduction',
--- which separately stores the kind coercion.
+-- such that @dco :: ty_in ~ ty_out@, where the role @r@ of the coercion
+-- is determined by the context.
--
-- See Note [The Reduction type].
data Reduction =
Reduction
- { reductionCoercion :: Coercion
- , reductionReducedType :: !Type
+ { reductionDCoercion :: DCoercion
+ , reductionReducedType :: !Type
}
--- N.B. the 'Coercion' field must be lazy: see for instance GHC.Tc.Solver.Rewrite.rewrite_tyvar2
--- which returns an error in the 'Coercion' field when dealing with a Derived constraint
--- (which is OK as this Coercion gets ignored later).
--- We might want to revisit the strictness once Deriveds are removed.
+-- NB: the DCoercion field is left lazy, as we might not have any need
+-- to look at it.
--- | Stores a heterogeneous reduction.
---
--- The stored kind coercion must relate the kinds of the
--- stored reduction. That is, in @HetReduction (Reduction co xi) kco@,
--- we must have:
---
--- > co :: ty ~ xi
--- > kco :: typeKind ty ~ typeKind xi
-data HetReduction =
- HetReduction
- Reduction
- MCoercionN
- -- N.B. strictness annotations don't seem to make a difference here
-
--- | Create a heterogeneous reduction.
---
--- Pre-condition: the provided kind coercion (second argument)
--- relates the kinds of the stored reduction.
--- That is, if the coercion stored in the 'Reduction' is of the form
---
--- > co :: ty ~ xi
---
--- Then the kind coercion supplied must be of the form:
---
--- > kco :: typeKind ty ~ typeKind xi
-mkHetReduction :: Reduction -- ^ heterogeneous reduction
- -> MCoercionN -- ^ kind coercion
- -> HetReduction
-mkHetReduction redn mco = HetReduction redn mco
-{-# INLINE mkHetReduction #-}
+-- | A 'Reduction' in which the 'Coercion' has 'Nominal' role.
+type ReductionN = Reduction
--- | Homogenise a heterogeneous reduction.
---
--- Given @HetReduction (Reduction co xi) kco@, with
---
--- > co :: ty ~ xi
--- > kco :: typeKind(ty) ~ typeKind(xi)
---
--- this returns the homogeneous reduction:
---
--- > hco :: ty ~ ( xi |> sym kco )
-homogeniseHetRedn :: Role -> HetReduction -> Reduction
-homogeniseHetRedn role (HetReduction redn kco)
- = mkCoherenceRightMRedn role redn (mkSymMCo kco)
-{-# INLINE homogeniseHetRedn #-}
+-- | A 'Reduction' in which the 'Coercion' has 'Representational' role.
+type ReductionR = Reduction
--- | Create a 'Reduction' from a pair of a 'Coercion' and a 'Type.
---
--- Pre-condition: the RHS type of the coercion matches the provided type
--- (perhaps up to zonking).
+-- | Create a 'Reduction' from a pair of a 'DCoercion' and a 'Type.
--
--- Use 'coercionRedn' when you only have the coercion.
-mkReduction :: Coercion -> Type -> Reduction
-mkReduction co ty = Reduction co ty
+-- Use 'mkDehydrateCoercionRedn' when you only have a 'Coercion'.
+mkReduction :: DCoercion -> Type -> Reduction
+mkReduction co rty = Reduction co rty
{-# INLINE mkReduction #-}
instance Outputable Reduction where
ppr redn =
braces $ vcat
- [ text "reductionOriginalType:" <+> ppr (reductionOriginalType redn)
- , text " reductionReducedType:" <+> ppr (reductionReducedType redn)
- , text " reductionCoercion:" <+> ppr (reductionCoercion redn)
+ [ text " reductionReducedType:" <+> ppr (reductionReducedType redn)
+ , text " reductionDCoercion:" <+> ppr (reductionDCoercion redn)
]
--- | A 'Reduction' in which the 'Coercion' has 'Nominal' role.
-type ReductionN = Reduction
+-- | Turn a 'Coercion' into a 'Reduction' by dehydrating.
+--
+-- Prefer using 'mkReduction' wherever possible to avoid doing these indirections.
+mkDehydrateCoercionRedn :: Coercion -> Reduction
+mkDehydrateCoercionRedn co =
+ Reduction (mkDehydrateCo co) (coercionRKind co)
+{-# INLINE mkDehydrateCoercionRedn #-}
--- | A 'Reduction' in which the 'Coercion' has 'Representational' role.
-type ReductionR = Reduction
+-- | Hydrate the 'DCoercion' stored inside a 'Reduction' into a full-fledged 'Coercion'.
+mkHydrateReductionDCoercion :: HasDebugCallStack
+ => Role
+ -> Type -- ^ LHS type (must not contain metavariables)
+ -> Reduction
+ -> Coercion
+mkHydrateReductionDCoercion r lty (Reduction dco rty) = mkHydrateDCo r lty dco rty
+{-# INLINE mkHydrateReductionDCoercion #-}
--- | Get the original, unreduced type corresponding to a 'Reduction'.
---
--- This is obtained by computing the LHS kind of the stored coercion,
--- which may be slow.
-reductionOriginalType :: Reduction -> Type
-reductionOriginalType = coercionLKind . reductionCoercion
-{-# INLINE reductionOriginalType #-}
-
--- | Turn a 'Coercion' into a 'Reduction'
--- by inspecting the RHS type of the coercion.
---
--- Prefer using 'mkReduction' when you already know
--- the RHS type of the coercion, to avoid computing it anew.
-coercionRedn :: Coercion -> Reduction
-coercionRedn co = Reduction co (coercionRKind co)
-{-# INLINE coercionRedn #-}
-
--- | Downgrade the role of the coercion stored in the 'Reduction'.
-downgradeRedn :: Role -- ^ desired role
- -> Role -- ^ current role
- -> Reduction
- -> Reduction
-downgradeRedn new_role old_role redn@(Reduction co _)
- = redn { reductionCoercion = downgradeRole new_role old_role co }
-{-# INLINE downgradeRedn #-}
-
--- | Downgrade the role of the coercion stored in the 'Reduction',
+-- | Downgrade the role of the directed coercion stored in the 'Reduction',
-- from 'Nominal' to 'Representational'.
-mkSubRedn :: Reduction -> Reduction
-mkSubRedn redn@(Reduction co _) = redn { reductionCoercion = mkSubCo co }
+mkSubRedn :: HasDebugCallStack => Type -> Reduction -> Reduction
+mkSubRedn lhs redn@(Reduction dco rhs)
+ = redn { reductionDCoercion = mkSubDCo lhs dco rhs }
{-# INLINE mkSubRedn #-}
--- | Compose a reduction with a coercion on the left.
+-- | Compose two reductions.
--
--- Pre-condition: the provided coercion's RHS type must match the LHS type
--- of the coercion that is stored in the reduction.
-mkTransRedn :: Coercion -> Reduction -> Reduction
-mkTransRedn co1 redn@(Reduction co2 _)
- = redn { reductionCoercion = co1 `mkTransCo` co2 }
+-- Assumes that forming a composite is valid, i.e. the RHS type of
+-- the first directed coercion equals, up to zonking,
+-- the LHS type of the second directed coercion.
+mkTransRedn :: Reduction -> Reduction -> Reduction
+mkTransRedn (Reduction dco1 _) (Reduction dco2 ty2)
+ = Reduction (dco1 `mkTransDCo` dco2) ty2
{-# INLINE mkTransRedn #-}
-- | The reflexive reduction.
-mkReflRedn :: Role -> Type -> Reduction
-mkReflRedn r ty = mkReduction (mkReflCo r ty) ty
+mkReflRedn :: Type -> Reduction
+mkReflRedn ty = mkReduction mkReflDCo ty
+{-# INLINE mkReflRedn #-}
-- | Create a 'Reduction' from a kind cast, in which
-- the casted type is the rewritten type.
@@ -226,10 +179,10 @@ mkReflRedn r ty = mkReduction (mkReflCo r ty) ty
-- Given @ty :: k1@, @mco :: k1 ~ k2@,
-- produces the 'Reduction' @ty ~res_co~> (ty |> mco)@
-- at the given 'Role'.
-mkGReflRightRedn :: Role -> Type -> CoercionN -> Reduction
-mkGReflRightRedn role ty co
+mkGReflRightRedn :: Type -> CoercionN -> Reduction
+mkGReflRightRedn ty co
= mkReduction
- (mkGReflRightCo role ty co)
+ (mkGReflRightDCo co)
(mkCastTy ty co)
{-# INLINE mkGReflRightRedn #-}
@@ -239,11 +192,13 @@ mkGReflRightRedn role ty co
-- Given @ty :: k1@, @mco :: k1 ~ k2@,
-- produces the 'Reduction' @ty ~res_co~> (ty |> mco)@
-- at the given 'Role'.
-mkGReflRightMRedn :: Role -> Type -> MCoercionN -> Reduction
-mkGReflRightMRedn role ty mco
+mkGReflRightMRedn :: Type -> MCoercionN -> Reduction
+mkGReflRightMRedn ty MRefl
+ = mkReflRedn ty
+mkGReflRightMRedn ty (MCo kco)
= mkReduction
- (mkGReflRightMCo role ty mco)
- (mkCastTyMCo ty mco)
+ (mkGReflRightDCo kco)
+ (mkCastTy ty kco)
{-# INLINE mkGReflRightMRedn #-}
-- | Create a 'Reduction' from a kind cast, in which
@@ -252,10 +207,10 @@ mkGReflRightMRedn role ty mco
-- Given @ty :: k1@, @mco :: k1 ~ k2@,
-- produces the 'Reduction' @(ty |> mco) ~res_co~> ty@
-- at the given 'Role'.
-mkGReflLeftRedn :: Role -> Type -> CoercionN -> Reduction
-mkGReflLeftRedn role ty co
+mkGReflLeftRedn :: Type -> CoercionN -> Reduction
+mkGReflLeftRedn ty co
= mkReduction
- (mkGReflLeftCo role ty co)
+ (mkGReflLeftDCo co)
ty
{-# INLINE mkGReflLeftRedn #-}
@@ -265,10 +220,12 @@ mkGReflLeftRedn role ty co
-- Given @ty :: k1@, @mco :: k1 ~ k2@,
-- produces the 'Reduction' @(ty |> mco) ~res_co~> ty@
-- at the given 'Role'.
-mkGReflLeftMRedn :: Role -> Type -> MCoercionN -> Reduction
-mkGReflLeftMRedn role ty mco
+mkGReflLeftMRedn :: Type -> MCoercionN -> Reduction
+mkGReflLeftMRedn ty MRefl
+ = mkReflRedn ty
+mkGReflLeftMRedn ty (MCo kco)
= mkReduction
- (mkGReflLeftMCo role ty mco)
+ (mkGReflLeftDCo kco)
ty
{-# INLINE mkGReflLeftMRedn #-}
@@ -278,10 +235,10 @@ mkGReflLeftMRedn role ty mco
-- with LHS kind @k2@, produce a new 'Reduction' @ty1 ~co2~> ( ty2 |> kco )@
-- of the given 'Role' (which must match the role of the coercion stored
-- in the 'Reduction' argument).
-mkCoherenceRightRedn :: Role -> Reduction -> CoercionN -> Reduction
-mkCoherenceRightRedn r (Reduction co1 ty2) kco
+mkCoherenceRightRedn :: Reduction -> CoercionN -> Reduction
+mkCoherenceRightRedn (Reduction co1 ty2) kco
= mkReduction
- (mkCoherenceRightCo r ty2 kco co1)
+ (mkCoherenceRightDCo kco co1)
(mkCastTy ty2 kco)
{-# INLINE mkCoherenceRightRedn #-}
@@ -291,11 +248,12 @@ mkCoherenceRightRedn r (Reduction co1 ty2) kco
-- with LHS kind @k2@, produce a new 'Reduction' @ty1 ~co2~> ( ty2 |> mco )@
-- of the given 'Role' (which must match the role of the coercion stored
-- in the 'Reduction' argument).
-mkCoherenceRightMRedn :: Role -> Reduction -> MCoercionN -> Reduction
-mkCoherenceRightMRedn r (Reduction co1 ty2) kco
+mkCoherenceRightMRedn :: Reduction -> MCoercionN -> Reduction
+mkCoherenceRightMRedn redn MRefl = redn
+mkCoherenceRightMRedn (Reduction co1 ty2) (MCo kco)
= mkReduction
- (mkCoherenceRightMCo r ty2 kco co1)
- (mkCastTyMCo ty2 kco)
+ (mkCoherenceRightDCo kco co1)
+ (mkCastTy ty2 kco)
{-# INLINE mkCoherenceRightMRedn #-}
-- | Apply a cast to a 'Reduction', casting both the original and the reduced type.
@@ -307,16 +265,14 @@ mkCoherenceRightMRedn r (Reduction co1 ty2) kco
--
-- Pre-condition: the 'Type' passed in is the same as the LHS type
-- of the coercion stored in the 'Reduction'.
-mkCastRedn1 :: Role
- -> Type -- ^ original type
- -> CoercionN -- ^ coercion to cast with
- -> Reduction -- ^ rewritten type, with rewriting coercion
+mkCastRedn1 :: CoercionN -- ^ coercion to cast with
+ -> Reduction -- ^ rewritten type, with rewriting coercion
-> Reduction
-mkCastRedn1 r ty cast_co (Reduction co xi)
+mkCastRedn1 cast_co (Reduction dco xi)
-- co :: ty ~r ty'
-- return_co :: (ty |> cast_co) ~r (ty' |> cast_co)
= mkReduction
- (castCoercionKind1 co r ty xi cast_co)
+ (castDCoercionKind1 dco cast_co)
(mkCastTy xi cast_co)
{-# INLINE mkCastRedn1 #-}
@@ -327,15 +283,13 @@ mkCastRedn1 r ty cast_co (Reduction co xi)
--
-- Pre-condition: the 'Type' passed in is the same as the LHS type
-- of the coercion stored in the 'Reduction'.
-mkCastRedn2 :: Role
- -> Type -- ^ original type
- -> CoercionN -- ^ coercion to cast with on the left
+mkCastRedn2 :: CoercionN -- ^ coercion to cast with on the left
-> Reduction -- ^ rewritten type, with rewriting coercion
-> CoercionN -- ^ coercion to cast with on the right
-> Reduction
-mkCastRedn2 r ty cast_co (Reduction nco nty) cast_co'
+mkCastRedn2 cast_co (Reduction nco nty) cast_co'
= mkReduction
- (castCoercionKind2 nco r ty nty cast_co cast_co')
+ (castDCoercionKind2 nco cast_co cast_co')
(mkCastTy nty cast_co')
{-# INLINE mkCastRedn2 #-}
@@ -343,26 +297,31 @@ mkCastRedn2 r ty cast_co (Reduction nco nty) cast_co'
--
-- Combines 'mkAppCo' and 'mkAppTy`.
mkAppRedn :: Reduction -> Reduction -> Reduction
-mkAppRedn (Reduction co1 ty1) (Reduction co2 ty2)
- = mkReduction (mkAppCo co1 co2) (mkAppTy ty1 ty2)
+mkAppRedn (Reduction co1 rty1) (Reduction co2 rty2)
+ = mkReduction
+ (mkAppDCo co1 co2)
+ (mkAppTy rty1 rty2)
{-# INLINE mkAppRedn #-}
-- | Create a function 'Reduction'.
--
-- Combines 'mkFunCo' and 'mkFunTy'.
-mkFunRedn :: Role
- -> FunTyFlag
+mkFunRedn :: FunTyFlag
-> ReductionN -- ^ multiplicity reduction
+ -> DCoercionN -- ^ argument 'RuntimeRep' coercion
+ -> DCoercionN -- ^ result 'RuntimeRep' coercion
-> Reduction -- ^ argument reduction
-> Reduction -- ^ result reduction
-> Reduction
-mkFunRedn r af
- (Reduction w_co w_ty)
- (Reduction arg_co arg_ty)
- (Reduction res_co res_ty)
+mkFunRedn af
+ (Reduction w_co w_rty)
+ arg_repco
+ res_repco
+ (Reduction arg_co arg_rty)
+ (Reduction res_co res_rty)
= mkReduction
- (mkFunCo r af w_co arg_co res_co)
- (mkFunTy af w_ty arg_ty res_ty)
+ (mkFunDCo af w_co arg_repco res_repco arg_co res_co)
+ (mkFunTy af w_rty arg_rty res_rty)
{-# INLINE mkFunRedn #-}
-- | Create a 'Reduction' associated to a Π type,
@@ -374,48 +333,44 @@ mkForAllRedn :: ForAllTyFlag
-> ReductionN -- ^ kind reduction
-> Reduction -- ^ body reduction
-> Reduction
-mkForAllRedn vis tv1 (Reduction h ki') (Reduction co ty)
+mkForAllRedn vis tv1 (Reduction h rki) (Reduction co rty)
= mkReduction
- (mkForAllCo tv1 h co)
- (mkForAllTy (Bndr tv2 vis) ty)
+ (mkForAllDCo tv1 h co)
+ (mkForAllTy (Bndr tv2 vis) rty)
where
- tv2 = setTyVarKind tv1 ki'
+ tv2 = setTyVarKind tv1 rki
{-# INLINE mkForAllRedn #-}
-- | Create a 'Reduction' of a quantified type from a
-- 'Reduction' of the body.
--
-- Combines 'mkHomoForAllCos' and 'mkForAllTys'.
-mkHomoForAllRedn :: [TyVarBinder] -> Reduction -> Reduction
-mkHomoForAllRedn bndrs (Reduction co ty)
+mkHomoForAllRedn :: [TyVarBinder] -> Type -> Reduction -> Reduction
+mkHomoForAllRedn bndrs ty1 (Reduction co ty2)
= mkReduction
- (mkHomoForAllCos (binderVars bndrs) co)
- (mkForAllTys bndrs ty)
+ (mkHomoForAllDCos (binderVars bndrs) (typeTypeOrConstraint ty1) co)
+ (mkForAllTys bndrs ty2)
{-# INLINE mkHomoForAllRedn #-}
-- | Create a 'Reduction' from a coercion between coercions.
--
-- Combines 'mkProofIrrelCo' and 'mkCoercionTy'.
-mkProofIrrelRedn :: Role -- ^ role of the created coercion, "r"
- -> CoercionN -- ^ co :: phi1 ~N phi2
- -> Coercion -- ^ g1 :: phi1
- -> Coercion -- ^ g2 :: phi2
- -> Reduction -- ^ res_co :: g1 ~r g2
-mkProofIrrelRedn role co g1 g2
+mkProofIrrelRedn :: Coercion -- ^ lhs_co
+ -> DCoercionN -- ^ dco :: lhs_co ~ rhs_co
+ -> Coercion -- ^ rhs_co
+ -> Reduction
+mkProofIrrelRedn _g1 co g2
= mkReduction
- (mkProofIrrelCo role co g1 g2)
- (mkCoercionTy g2)
+ (mkProofIrrelDCo co rhs_co)
+ rhs_co
+ where
+ rhs_co = mkCoercionTy g2
{-# INLINE mkProofIrrelRedn #-}
--- | Create a reflexive 'Reduction' whose RHS is the given 'Coercion',
+-- | Create a reflexive 'Reduction' whose LHS and RHS is the given 'Coercion',
-- with the specified 'Role'.
-mkReflCoRedn :: Role -> Coercion -> Reduction
-mkReflCoRedn role co
- = mkReduction
- (mkReflCo role co_ty)
- co_ty
- where
- co_ty = mkCoercionTy co
+mkReflCoRedn :: Coercion -> Reduction
+mkReflCoRedn co = mkReduction mkReflDCo (mkCoercionTy co)
{-# INLINE mkReflCoRedn #-}
-- | A collection of 'Reduction's where the coercions and the types are stored separately.
@@ -425,36 +380,72 @@ mkReflCoRedn role co
-- This datatype is used in 'mkAppRedns', 'mkClassPredRedns' and 'mkTyConAppRedn',
-- which expect separate types and coercions.
--
--- Invariant: the two stored lists are of the same length,
--- and the RHS type of each coercion is the corresponding type.
-data Reductions = Reductions [Coercion] [Type]
+-- Invariant: given @Reductions lhs_tys dcos rhs_tys@, and an ambient role @r@,
+-- we can obtain the @rhs_tys@ by following the directed coercions starting from the repsective
+-- @lhs_tys@. Equivalently, @zipWith (followDCo r) lhs_tys dcos@ is equal (up to zonking) to @rhs_tys@.
+data Reductions = Reductions [DCoercion] [Type]
+
+instance Outputable Reductions where
+ ppr (Reductions dcos rtys) = parens (text "Reductions" <+> ppr dcos <+> ppr rtys)
-- | Create 'Reductions' from individual lists of coercions and types.
--
-- The lists should be of the same length, and the RHS type of each coercion
-- should match the specified type in the other list.
-mkReductions :: [Coercion] -> [Type] -> Reductions
-mkReductions cos tys = Reductions cos tys
+mkReductions :: [DCoercion] -> [Type] -> Reductions
+mkReductions cos tys2 = Reductions cos tys2
{-# INLINE mkReductions #-}
+mkReflRedns :: [Type] -> Reductions
+mkReflRedns tys = mkReductions (mkReflDCos tys) tys
+{-# INLINE mkReflRedns #-}
+
+mkReflDCos :: [Type] -> [DCoercion]
+mkReflDCos tys = replicate (length tys) mkReflDCo
+{-# INLINE mkReflDCos #-}
+
-- | Combines 'mkAppCos' and 'mkAppTys'.
mkAppRedns :: Reduction -> Reductions -> Reduction
-mkAppRedns (Reduction co ty) (Reductions cos tys)
- = mkReduction (mkAppCos co cos) (mkAppTys ty tys)
+mkAppRedns (Reduction co ty2) (Reductions cos tys2)
+ = mkReduction (mkAppDCos co cos) (mkAppTys ty2 tys2)
{-# INLINE mkAppRedns #-}
-- | 'TyConAppCo' for 'Reduction's: combines 'mkTyConAppCo' and `mkTyConApp`.
-mkTyConAppRedn :: Role -> TyCon -> Reductions -> Reduction
-mkTyConAppRedn role tc (Reductions cos tys)
- = mkReduction (mkTyConAppCo role tc cos) (mkTyConApp tc tys)
+--
+-- Use this when you know the 'TyCon' is not a type synonym. If it might be,
+-- use 'mkTyConAppRedn_MightBeSynonym'.
+mkTyConAppRedn :: TyCon -> Reductions -> Reduction
+mkTyConAppRedn tc (Reductions cos tys2)
+ = mkReduction (mkTyConAppDCo cos) (mkTyConApp tc tys2)
{-# INLINE mkTyConAppRedn #-}
+-- | 'TyConAppCo' for 'Reduction's: combines 'mkTyConAppCo' and `mkTyConApp`.
+--
+-- Use 'mkTyConAppRedn' if the 'TyCon' is definitely not a type synonym.
+mkTyConAppRedn_MightBeSynonym :: Role -> TyCon -> [Type] -> Reductions -> Reduction
+mkTyConAppRedn_MightBeSynonym role tc tys1 redns@(Reductions dcos tys2)
+ -- 'mkTyConAppCo' handles synomyms by using substitution lifting.
+ -- We don't have that for directed coercions, so we use hydrate/dehydrate
+ -- so that we can call 'liftCoSubst'.
+ -- In the future, it might be desirable to implement substitution lifting
+ -- for directed coercions to avoid this (and a similar issue in simplifyArgsWorker).
+ | ExpandsSyn tv_dco_prs rhs_ty leftover_dcos <- expandSynTyCon_maybe tc dcos
+ , let tv_co_prs = zipWith4 hydrate (tyConRoleListX role tc) tys1 tv_dco_prs tys2
+ = mkReduction
+ (mkAppDCos (mkDehydrateCo $ liftCoSubst role (mkLiftingContext tv_co_prs) rhs_ty) leftover_dcos)
+ (mkTyConApp tc tys2)
+ | otherwise = mkTyConAppRedn tc redns
+ where
+ hydrate r l (tv,dco) t = (tv, mkHydrateDCo r l dco t)
+ {-# INLINE hydrate #-}
+{-# INLINE mkTyConAppRedn_MightBeSynonym #-}
+
-- | Reduce the arguments of a 'Class' 'TyCon'.
mkClassPredRedn :: Class -> Reductions -> Reduction
-mkClassPredRedn cls (Reductions cos tys)
+mkClassPredRedn cls (Reductions cos tys2)
= mkReduction
- (mkTyConAppCo Nominal (classTyCon cls) cos)
- (mkClassPred cls tys)
+ (mkTyConAppDCo cos)
+ (mkClassPred cls tys2)
{-# INLINE mkClassPredRedn #-}
-- | Obtain 'Reductions' from a list of 'Reduction's by unzipping.
@@ -471,7 +462,7 @@ unzipRedns = foldr accRedn (Reductions [] [])
--
-- unzipRedns <$> zipWithM f tys roles
--
--- - GHC.Tc.Solver.Monad.breakTyEqCycle_maybe, with two calls of the form:
+-- - GHC.Tc.Solver.Monad.breakTyVarCycle_maybe, with two calls of the form:
--
-- unzipRedns <$> mapM f tys
--
@@ -479,6 +470,67 @@ unzipRedns = foldr accRedn (Reductions [] [])
-- but the above locations aren't performance critical, so it was deemed
-- to not be worth it.
+-- | Stores a heterogeneous reduction.
+--
+-- The stored kind coercion must relate the kinds of the
+-- stored reduction. That is, in @HetReduction (Reduction co xi) kco@,
+-- we must have:
+--
+-- > co :: ty ~ xi
+-- > kco :: typeKind ty ~ typeKind xi
+data HetReduction =
+ HetReduction
+ Reduction
+ MCoercionN
+ -- N.B. strictness annotations don't seem to make a difference here
+
+-- | Create a heterogeneous reduction.
+--
+-- Pre-condition: the provided kind coercion (second argument)
+-- relates the kinds of the stored reduction.
+-- That is, if the coercion stored in the 'Reduction' is of the form
+--
+-- > co :: ty ~ xi
+--
+-- Then the kind coercion supplied must be of the form:
+--
+-- > kco :: typeKind ty ~ typeKind xi
+mkHetReduction :: Reduction -- ^ heterogeneous reduction
+ -> MCoercionN -- ^ kind coercion
+ -> HetReduction
+mkHetReduction redn mco = HetReduction redn mco
+{-# INLINE mkHetReduction #-}
+
+-- | Homogenise a heterogeneous reduction.
+--
+-- Given @HetReduction (Reduction co xi) kco@, with
+--
+-- > co :: ty ~ xi
+-- > kco :: typeKind(ty) ~ typeKind(xi)
+--
+-- this returns the homogeneous reduction:
+--
+-- > hco :: ty ~ ( xi |> sym kco )
+homogeniseHetRedn :: HetReduction -> Reduction
+homogeniseHetRedn (HetReduction redn kco)
+ = mkCoherenceRightMRedn redn (mkSymMCo kco)
+{-# INLINE homogeniseHetRedn #-}
+
+-- | Homogenise a reduction.
+--
+-- Given @redn = Reduction co xi@ and kind coercion @kco@, with
+--
+-- > co :: ty ~ xi
+-- > kco :: typeKind(ty) ~ typeKind(xi)
+--
+-- this returns the homogeneous reduction:
+--
+-- > hco :: ty ~ ( xi |> sym kco )
+homogeniseRedn :: Reduction -> MCoercionN -> Reduction
+homogeniseRedn redn mco
+ = mkCoherenceRightMRedn redn (mkSymMCo mco)
+{-# INLINE homogeniseRedn #-}
+
{-
%************************************************************************
%* *
@@ -779,6 +831,9 @@ data ArgsReductions =
-- The strictness annotations and UNPACK pragma here are crucial
-- to getting good performance in simplifyArgsWorker's tight loop.
+instance Outputable ArgsReductions where
+ ppr (ArgsReductions redns kco) = parens (text "ArgsReductions" <+> ppr redns <+> ppr kco)
+
-- This is shared between the rewriter and the normaliser in GHC.Core.FamInstEnv.
-- See Note [simplifyArgsWorker]
{-# INLINE simplifyArgsWorker #-}
@@ -791,6 +846,7 @@ simplifyArgsWorker :: HasDebugCallStack
-- list of binders can be shorter or longer than the list of args
-> TyCoVarSet -- free vars of the args
-> Infinite Role-- list of roles, r
+ -> [Type] -- original type arguments ty_i
-> [Reduction] -- rewritten type arguments, arg_i
-- each comes with the coercion used to rewrite it,
-- arg_co_i :: ty_i ~ arg_i
@@ -804,10 +860,10 @@ simplifyArgsWorker :: HasDebugCallStack
-- function is all about. That is, (f xi_1 ... xi_n), where xi_i are the returned arguments,
-- *is* well kinded.
simplifyArgsWorker orig_ki_binders orig_inner_ki orig_fvs
- orig_roles orig_simplified_args
+ orig_roles tys redns
= go orig_lc
orig_ki_binders orig_inner_ki
- orig_roles orig_simplified_args
+ orig_roles (zip tys redns)
where
orig_lc = emptyLiftingContext $ mkInScopeSet orig_fvs
@@ -815,7 +871,7 @@ simplifyArgsWorker orig_ki_binders orig_inner_ki orig_fvs
-> [PiTyBinder] -- Unsubsted binders of function's kind
-> Kind -- Unsubsted result kind of function (not a Pi-type)
-> Infinite Role -- Roles at which to rewrite these ...
- -> [Reduction] -- rewritten arguments, with their rewriting coercions
+ -> [(Type, Reduction)] -- rewritten arguments, with their rewriting coercions
-> ArgsReductions
go !lc binders inner_ki _ []
-- The !lc makes the function strict in the lifting context
@@ -828,7 +884,7 @@ simplifyArgsWorker orig_ki_binders orig_inner_ki orig_fvs
kind_co | noFreeVarsOfType final_kind = MRefl
| otherwise = MCo $ liftCoSubst Nominal lc final_kind
- go lc (binder:binders) inner_ki (Inf role roles) (arg_redn:arg_redns)
+ go lc (binder:binders) inner_ki (Inf role roles) ((orig_ty,arg_redn):arg_redns)
= -- We rewrite an argument ty with arg_redn = Reduction arg_co arg
-- By Note [Rewriting] in GHC.Tc.Solver.Rewrite invariant (F2),
-- typeKind(ty) = typeKind(arg).
@@ -841,10 +897,11 @@ simplifyArgsWorker orig_ki_binders orig_inner_ki orig_fvs
-- significantly in optimized builds; see #18502
let !kind_co = liftCoSubst Nominal lc (piTyBinderType binder)
!(Reduction casted_co casted_xi)
- = mkCoherenceRightRedn role arg_redn kind_co
+ = mkCoherenceRightRedn arg_redn kind_co
-- now, extend the lifting context with the new binding
!new_lc | Just tv <- namedPiTyBinder_maybe binder
- = extendLiftingContextAndInScope lc tv casted_co
+ = extendLiftingContextAndInScope lc tv
+ $ mkHydrateDCo role orig_ty casted_co casted_xi
| otherwise
= lc
!(ArgsReductions (Reductions cos xis) final_kind_co)
@@ -854,14 +911,14 @@ simplifyArgsWorker orig_ki_binders orig_inner_ki orig_fvs
final_kind_co
-- See Note [Last case in simplifyArgsWorker]
- go lc [] inner_ki roles arg_redns
+ go lc [] inner_ki roles arg_tys_and_redns
= let co1 = liftCoSubst Nominal lc inner_ki
+ (orig_tys, arg_redns) = unzip arg_tys_and_redns
co1_kind = coercionKind co1
- unrewritten_tys = map reductionOriginalType arg_redns
- (arg_cos, res_co) = decomposePiCos co1 co1_kind unrewritten_tys
- casted_args = assertPpr (equalLength arg_redns arg_cos)
+ (arg_cos, res_co) = decomposePiCos co1 co1_kind orig_tys
+ casted_redns = assertPpr (equalLength arg_redns arg_cos)
(ppr arg_redns $$ ppr arg_cos)
- $ zipWith3 mkCoherenceRightRedn (Inf.toList roles) arg_redns arg_cos
+ $ zipWith mkCoherenceRightRedn arg_redns arg_cos
-- In general decomposePiCos can return fewer cos than tys,
-- but not here; because we're well typed, there will be enough
-- binders. Note that decomposePiCos does substitutions, so even
@@ -873,6 +930,6 @@ simplifyArgsWorker orig_ki_binders orig_inner_ki orig_fvs
(bndrs, new_inner) = splitPiTys rewritten_kind
ArgsReductions redns_out res_co_out
- = go zapped_lc bndrs new_inner roles casted_args
+ = go zapped_lc bndrs new_inner roles (zip orig_tys casted_redns)
in
ArgsReductions redns_out (res_co `mkTransMCoR` res_co_out)
diff --git a/compiler/GHC/Core/SimpleOpt.hs b/compiler/GHC/Core/SimpleOpt.hs
index b751b10206..8d80a072a4 100644
--- a/compiler/GHC/Core/SimpleOpt.hs
+++ b/compiler/GHC/Core/SimpleOpt.hs
@@ -98,8 +98,9 @@ data SimpleOpts = SimpleOpts
defaultSimpleOpts :: SimpleOpts
defaultSimpleOpts = SimpleOpts
{ so_uf_opts = defaultUnfoldingOpts
- , so_co_opts = OptCoercionOpts { optCoercionEnabled = False }
, so_eta_red = False
+ , so_co_opts = OptCoercionOpts { optCoercionOpts = Nothing }
+ -- Nothing <=> no coercion optimisation
}
simpleOptExpr :: HasDebugCallStack => SimpleOpts -> CoreExpr -> CoreExpr
diff --git a/compiler/GHC/Core/TyCo/FVs.hs b/compiler/GHC/Core/TyCo/FVs.hs
index aa9c04c46b..e81b711171 100644
--- a/compiler/GHC/Core/TyCo/FVs.hs
+++ b/compiler/GHC/Core/TyCo/FVs.hs
@@ -13,22 +13,28 @@ module GHC.Core.TyCo.FVs
shallowTyCoVarsOfTyVarEnv, shallowTyCoVarsOfCoVarEnv,
shallowTyCoVarsOfCo, shallowTyCoVarsOfCos,
+ shallowTyCoVarsOfDCo, shallowTyCoVarsOfDCos,
tyCoVarsOfCo, tyCoVarsOfCos, tyCoVarsOfMCo,
+ tyCoVarsOfDCo,
coVarsOfType, coVarsOfTypes,
coVarsOfCo, coVarsOfCos,
tyCoVarsOfCoDSet,
tyCoFVsOfCo, tyCoFVsOfCos,
tyCoVarsOfCoList,
+ tyCoVarsOfDCoList,
almostDevoidCoVarOfCo,
+ almostDevoidCoVarOfDCo,
-- Injective free vars
injectiveVarsOfType, injectiveVarsOfTypes, isInjectiveInType,
invisibleVarsOfType, invisibleVarsOfTypes,
-- Any and No Free vars
- anyFreeVarsOfType, anyFreeVarsOfTypes, anyFreeVarsOfCo,
- noFreeVarsOfType, noFreeVarsOfTypes, noFreeVarsOfCo,
+ anyFreeVarsOfType, anyFreeVarsOfTypes,
+ anyFreeVarsOfCo, anyFreeVarsOfDCo,
+ noFreeVarsOfType, noFreeVarsOfTypes,
+ noFreeVarsOfCo, noFreeVarsOfDCo,
-- * Free type constructors
tyConsOfType, tyConsOfTypes,
@@ -309,11 +315,15 @@ tyCoVarsOfMCo (MCo co) = tyCoVarsOfCo co
tyCoVarsOfCos :: [Coercion] -> TyCoVarSet
tyCoVarsOfCos cos = runTyCoVars (deep_cos cos)
+tyCoVarsOfDCo :: DCoercion -> TyCoVarSet
+tyCoVarsOfDCo co = runTyCoVars (deep_dco co)
+
deep_ty :: Type -> Endo TyCoVarSet
deep_tys :: [Type] -> Endo TyCoVarSet
deep_co :: Coercion -> Endo TyCoVarSet
deep_cos :: [Coercion] -> Endo TyCoVarSet
-(deep_ty, deep_tys, deep_co, deep_cos) = foldTyCo deepTcvFolder emptyVarSet
+deep_dco :: DCoercion -> Endo TyCoVarSet
+(deep_ty, deep_tys, deep_co, deep_cos, deep_dco, _) = foldTyCo deepTcvFolder emptyVarSet
deepTcvFolder :: TyCoFolder TyCoVarSet (Endo TyCoVarSet)
deepTcvFolder = TyCoFolder { tcf_view = noView
@@ -353,6 +363,12 @@ shallowTyCoVarsOfCo co = runTyCoVars (shallow_co co)
shallowTyCoVarsOfCos :: [Coercion] -> TyCoVarSet
shallowTyCoVarsOfCos cos = runTyCoVars (shallow_cos cos)
+shallowTyCoVarsOfDCo :: DCoercion -> TyCoVarSet
+shallowTyCoVarsOfDCo dco = runTyCoVars (shallow_dco dco)
+
+shallowTyCoVarsOfDCos :: [DCoercion] -> TyCoVarSet
+shallowTyCoVarsOfDCos dcos = runTyCoVars (shallow_dcos dcos)
+
-- | Returns free variables of types, including kind variables as
-- a non-deterministic set. For type synonyms it does /not/ expand the
-- synonym.
@@ -371,7 +387,9 @@ shallow_ty :: Type -> Endo TyCoVarSet
shallow_tys :: [Type] -> Endo TyCoVarSet
shallow_co :: Coercion -> Endo TyCoVarSet
shallow_cos :: [Coercion] -> Endo TyCoVarSet
-(shallow_ty, shallow_tys, shallow_co, shallow_cos) = foldTyCo shallowTcvFolder emptyVarSet
+shallow_dco :: DCoercion -> Endo TyCoVarSet
+shallow_dcos :: [DCoercion] -> Endo TyCoVarSet
+(shallow_ty, shallow_tys, shallow_co, shallow_cos, shallow_dco, shallow_dcos) = foldTyCo shallowTcvFolder emptyVarSet
shallowTcvFolder :: TyCoFolder TyCoVarSet (Endo TyCoVarSet)
shallowTcvFolder = TyCoFolder { tcf_view = noView
@@ -420,7 +438,7 @@ deep_cv_ty :: Type -> Endo CoVarSet
deep_cv_tys :: [Type] -> Endo CoVarSet
deep_cv_co :: Coercion -> Endo CoVarSet
deep_cv_cos :: [Coercion] -> Endo CoVarSet
-(deep_cv_ty, deep_cv_tys, deep_cv_co, deep_cv_cos) = foldTyCo deepCoVarFolder emptyVarSet
+(deep_cv_ty, deep_cv_tys, deep_cv_co, deep_cv_cos, _, _) = foldTyCo deepCoVarFolder emptyVarSet
deepCoVarFolder :: TyCoFolder TyCoVarSet (Endo CoVarSet)
deepCoVarFolder = TyCoFolder { tcf_view = noView
@@ -617,6 +635,9 @@ tyCoVarsOfCoList :: Coercion -> [TyCoVar]
-- See Note [Free variables of types]
tyCoVarsOfCoList co = fvVarList $ tyCoFVsOfCo co
+tyCoVarsOfDCoList :: DCoercion -> [TyCoVar]
+tyCoVarsOfDCoList co = fvVarList $ tyCoFVsOfDCo co
+
tyCoFVsOfMCo :: MCoercion -> FV
tyCoFVsOfMCo MRefl = emptyFV
tyCoFVsOfMCo (MCo co) = tyCoFVsOfCo co
@@ -641,9 +662,12 @@ tyCoFVsOfCo (HoleCo h) fv_cand in_scope acc
= tyCoFVsOfCoVar (coHoleCoVar h) fv_cand in_scope acc
-- See Note [CoercionHoles and coercion free variables]
tyCoFVsOfCo (AxiomInstCo _ _ cos) fv_cand in_scope acc = tyCoFVsOfCos cos fv_cand in_scope acc
+tyCoFVsOfCo (HydrateDCo _ t1 dco _) fv_cand in_scope acc
+ = (tyCoFVsOfType t1 `unionFV` tyCoFVsOfDCo dco) fv_cand in_scope acc
tyCoFVsOfCo (UnivCo p _ t1 t2) fv_cand in_scope acc
- = (tyCoFVsOfProv p `unionFV` tyCoFVsOfType t1
- `unionFV` tyCoFVsOfType t2) fv_cand in_scope acc
+ = (tyCoFVsOfProv tyCoFVsOfCo p
+ `unionFV` tyCoFVsOfType t1
+ `unionFV` tyCoFVsOfType t2) fv_cand in_scope acc
tyCoFVsOfCo (SymCo co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
tyCoFVsOfCo (TransCo co1 co2) fv_cand in_scope acc = (tyCoFVsOfCo co1 `unionFV` tyCoFVsOfCo co2) fv_cand in_scope acc
tyCoFVsOfCo (SelCo _ co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
@@ -657,16 +681,35 @@ tyCoFVsOfCoVar :: CoVar -> FV
tyCoFVsOfCoVar v fv_cand in_scope acc
= (unitFV v `unionFV` tyCoFVsOfType (varType v)) fv_cand in_scope acc
-tyCoFVsOfProv :: UnivCoProvenance -> FV
-tyCoFVsOfProv (PhantomProv co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
-tyCoFVsOfProv (ProofIrrelProv co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
-tyCoFVsOfProv (PluginProv _) fv_cand in_scope acc = emptyFV fv_cand in_scope acc
-tyCoFVsOfProv (CorePrepProv _) fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+tyCoFVsOfProv :: (co -> FV) -> UnivCoProvenance co -> FV
+tyCoFVsOfProv tyCoFVs_of_co (PhantomProv co) fv_cand in_scope acc = tyCoFVs_of_co co fv_cand in_scope acc
+tyCoFVsOfProv tyCoFVs_of_co (ProofIrrelProv co) fv_cand in_scope acc = tyCoFVs_of_co co fv_cand in_scope acc
+tyCoFVsOfProv _ (PluginProv _) fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+tyCoFVsOfProv _ (CorePrepProv _) fv_cand in_scope acc = emptyFV fv_cand in_scope acc
tyCoFVsOfCos :: [Coercion] -> FV
tyCoFVsOfCos [] fv_cand in_scope acc = emptyFV fv_cand in_scope acc
tyCoFVsOfCos (co:cos) fv_cand in_scope acc = (tyCoFVsOfCo co `unionFV` tyCoFVsOfCos cos) fv_cand in_scope acc
+tyCoFVsOfDCos :: [DCoercion] -> FV
+tyCoFVsOfDCos [] fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+tyCoFVsOfDCos (co:cos) fv_cand in_scope acc = (tyCoFVsOfDCo co `unionFV` tyCoFVsOfDCos cos) fv_cand in_scope acc
+
+tyCoFVsOfDCo :: DCoercion -> FV
+tyCoFVsOfDCo ReflDCo fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+tyCoFVsOfDCo (GReflRightDCo co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
+tyCoFVsOfDCo (GReflLeftDCo co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
+tyCoFVsOfDCo (TyConAppDCo dcos) fv_cand in_scope acc = tyCoFVsOfDCos dcos fv_cand in_scope acc
+tyCoFVsOfDCo (AppDCo dco1 dco2) fv_cand in_scope acc = (tyCoFVsOfDCo dco1 `unionFV` tyCoFVsOfDCo dco2) fv_cand in_scope acc
+tyCoFVsOfDCo (ForAllDCo tv kind_dco co) fv_cand in_scope acc
+ = (tyCoFVsVarBndr tv (tyCoFVsOfDCo co) `unionFV` tyCoFVsOfDCo kind_dco) fv_cand in_scope acc
+tyCoFVsOfDCo (CoVarDCo v) fv_cand in_scope acc = tyCoFVsOfCoVar v fv_cand in_scope acc
+tyCoFVsOfDCo AxiomInstDCo{} fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+tyCoFVsOfDCo StepsDCo{} fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+tyCoFVsOfDCo (TransDCo dco1 dco2) fv_cand in_scope acc = (tyCoFVsOfDCo dco1 `unionFV` tyCoFVsOfDCo dco2) fv_cand in_scope acc
+tyCoFVsOfDCo (DehydrateCo co) fv_cand in_scope acc = tyCoFVsOfCo co fv_cand in_scope acc
+tyCoFVsOfDCo (UnivDCo p rhs) fv_cand in_scope acc = (tyCoFVsOfProv tyCoFVsOfDCo p `unionFV` tyCoFVsOfType rhs) fv_cand in_scope acc
+tyCoFVsOfDCo (SubDCo dco) fv_cand in_scope acc = tyCoFVsOfDCo dco fv_cand in_scope acc
----- Whether a covar is /Almost Devoid/ in a type or coercion ----
@@ -677,6 +720,10 @@ almostDevoidCoVarOfCo :: CoVar -> Coercion -> Bool
almostDevoidCoVarOfCo cv co =
almost_devoid_co_var_of_co co cv
+almostDevoidCoVarOfDCo :: CoVar -> DCoercion -> Bool
+almostDevoidCoVarOfDCo cv dco =
+ almost_devoid_co_var_of_dco dco cv
+
almost_devoid_co_var_of_co :: Coercion -> CoVar -> Bool
almost_devoid_co_var_of_co (Refl {}) _ = True -- covar is allowed in Refl and
almost_devoid_co_var_of_co (GRefl {}) _ = True -- GRefl, so we don't look into
@@ -697,8 +744,11 @@ almost_devoid_co_var_of_co (CoVarCo v) cv = v /= cv
almost_devoid_co_var_of_co (HoleCo h) cv = (coHoleCoVar h) /= cv
almost_devoid_co_var_of_co (AxiomInstCo _ _ cos) cv
= almost_devoid_co_var_of_cos cos cv
+almost_devoid_co_var_of_co (HydrateDCo _ t1 dco _) cv
+ = almost_devoid_co_var_of_type t1 cv
+ && almost_devoid_co_var_of_dco dco cv
almost_devoid_co_var_of_co (UnivCo p _ t1 t2) cv
- = almost_devoid_co_var_of_prov p cv
+ = almost_devoid_co_var_of_prov almost_devoid_co_var_of_co p cv
&& almost_devoid_co_var_of_type t1 cv
&& almost_devoid_co_var_of_type t2 cv
almost_devoid_co_var_of_co (SymCo co) cv
@@ -726,13 +776,46 @@ almost_devoid_co_var_of_cos (co:cos) cv
= almost_devoid_co_var_of_co co cv
&& almost_devoid_co_var_of_cos cos cv
-almost_devoid_co_var_of_prov :: UnivCoProvenance -> CoVar -> Bool
-almost_devoid_co_var_of_prov (PhantomProv co) cv
- = almost_devoid_co_var_of_co co cv
-almost_devoid_co_var_of_prov (ProofIrrelProv co) cv
+almost_devoid_co_var_of_dcos :: [DCoercion] -> CoVar -> Bool
+almost_devoid_co_var_of_dcos [] _ = True
+almost_devoid_co_var_of_dcos (co:cos) cv
+ = almost_devoid_co_var_of_dco co cv
+ && almost_devoid_co_var_of_dcos cos cv
+
+almost_devoid_co_var_of_dco :: DCoercion -> CoVar -> Bool
+almost_devoid_co_var_of_dco ReflDCo _ = True
+almost_devoid_co_var_of_dco GReflRightDCo{} _ = True -- GRefl, so we don't look into
+ -- the coercions
+almost_devoid_co_var_of_dco GReflLeftDCo{} _ = True
+almost_devoid_co_var_of_dco (TyConAppDCo cos) cv
+ = almost_devoid_co_var_of_dcos cos cv
+almost_devoid_co_var_of_dco (AppDCo co arg) cv
+ = almost_devoid_co_var_of_dco co cv
+ && almost_devoid_co_var_of_dco arg cv
+almost_devoid_co_var_of_dco (ForAllDCo v kind_co co) cv
+ = almost_devoid_co_var_of_dco kind_co cv
+ && (v == cv || almost_devoid_co_var_of_dco co cv)
+almost_devoid_co_var_of_dco (CoVarDCo v) cv = v /= cv
+almost_devoid_co_var_of_dco AxiomInstDCo{} _ = True
+almost_devoid_co_var_of_dco StepsDCo{} _ = True
+almost_devoid_co_var_of_dco (TransDCo co1 co2) cv
+ = almost_devoid_co_var_of_dco co1 cv
+ && almost_devoid_co_var_of_dco co2 cv
+almost_devoid_co_var_of_dco (DehydrateCo co) cv
= almost_devoid_co_var_of_co co cv
-almost_devoid_co_var_of_prov (PluginProv _) _ = True
-almost_devoid_co_var_of_prov (CorePrepProv _) _ = True
+almost_devoid_co_var_of_dco (UnivDCo prov rhs) cv
+ = almost_devoid_co_var_of_prov almost_devoid_co_var_of_dco prov cv
+ && almost_devoid_co_var_of_type rhs cv
+almost_devoid_co_var_of_dco (SubDCo dco) cv
+ = almost_devoid_co_var_of_dco dco cv
+
+almost_devoid_co_var_of_prov :: (co -> CoVar -> Bool) -> UnivCoProvenance co -> CoVar -> Bool
+almost_devoid_co_var_of_prov almost_devoid_co (PhantomProv co) cv
+ = almost_devoid_co co cv
+almost_devoid_co_var_of_prov almost_devoid_co (ProofIrrelProv co) cv
+ = almost_devoid_co co cv
+almost_devoid_co_var_of_prov _ (PluginProv _) _ = True
+almost_devoid_co_var_of_prov _ (CorePrepProv _) _ = True
almost_devoid_co_var_of_type :: Type -> CoVar -> Bool
almost_devoid_co_var_of_type (TyVarTy _) _ = True
@@ -953,27 +1036,35 @@ afvFolder check_fv = TyCoFolder { tcf_view = noView
anyFreeVarsOfType :: (TyCoVar -> Bool) -> Type -> Bool
anyFreeVarsOfType check_fv ty = DM.getAny (f ty)
- where (f, _, _, _) = foldTyCo (afvFolder check_fv) emptyVarSet
+ where (f, _, _, _, _, _) = foldTyCo (afvFolder check_fv) emptyVarSet
anyFreeVarsOfTypes :: (TyCoVar -> Bool) -> [Type] -> Bool
anyFreeVarsOfTypes check_fv tys = DM.getAny (f tys)
- where (_, f, _, _) = foldTyCo (afvFolder check_fv) emptyVarSet
+ where (_, f, _, _, _, _) = foldTyCo (afvFolder check_fv) emptyVarSet
anyFreeVarsOfCo :: (TyCoVar -> Bool) -> Coercion -> Bool
anyFreeVarsOfCo check_fv co = DM.getAny (f co)
- where (_, _, f, _) = foldTyCo (afvFolder check_fv) emptyVarSet
+ where (_, _, f, _, _, _) = foldTyCo (afvFolder check_fv) emptyVarSet
+
+anyFreeVarsOfDCo :: (TyCoVar -> Bool) -> DCoercion -> Bool
+anyFreeVarsOfDCo check_fv co = DM.getAny (f co)
+ where (_, _, _, _, f, _) = foldTyCo (afvFolder check_fv) emptyVarSet
noFreeVarsOfType :: Type -> Bool
noFreeVarsOfType ty = not $ DM.getAny (f ty)
- where (f, _, _, _) = foldTyCo (afvFolder (const True)) emptyVarSet
+ where (f, _, _, _, _, _) = foldTyCo (afvFolder (const True)) emptyVarSet
noFreeVarsOfTypes :: [Type] -> Bool
noFreeVarsOfTypes tys = not $ DM.getAny (f tys)
- where (_, f, _, _) = foldTyCo (afvFolder (const True)) emptyVarSet
+ where (_, f, _, _, _, _) = foldTyCo (afvFolder (const True)) emptyVarSet
noFreeVarsOfCo :: Coercion -> Bool
noFreeVarsOfCo co = not $ DM.getAny (f co)
- where (_, _, f, _) = foldTyCo (afvFolder (const True)) emptyVarSet
+ where (_, _, f, _, _, _) = foldTyCo (afvFolder (const True)) emptyVarSet
+
+noFreeVarsOfDCo :: DCoercion -> Bool
+noFreeVarsOfDCo dco = not $ DM.getAny (f dco)
+ where (_, _, _, _, f, _) = foldTyCo (afvFolder (const True)) emptyVarSet
{- *********************************************************************
@@ -1113,7 +1204,8 @@ tyConsOfType ty
go_co (FunCo { fco_mult = m, fco_arg = a, fco_res = r })
= go_co m `unionUniqSets` go_co a `unionUniqSets` go_co r
go_co (AxiomInstCo ax _ args) = go_ax ax `unionUniqSets` go_cos args
- go_co (UnivCo p _ t1 t2) = go_prov p `unionUniqSets` go t1 `unionUniqSets` go t2
+ go_co (UnivCo p _ t1 t2) = go_prov go_co p `unionUniqSets` go t1 `unionUniqSets` go t2
+ go_co (HydrateDCo _ t1 dco _) = go t1 `unionUniqSets` go_dco dco
go_co (CoVarCo {}) = emptyUniqSet
go_co (HoleCo {}) = emptyUniqSet
go_co (SymCo co) = go_co co
@@ -1128,14 +1220,29 @@ tyConsOfType ty
go_mco MRefl = emptyUniqSet
go_mco (MCo co) = go_co co
- go_prov (PhantomProv co) = go_co co
- go_prov (ProofIrrelProv co) = go_co co
- go_prov (PluginProv _) = emptyUniqSet
- go_prov (CorePrepProv _) = emptyUniqSet
+ go_dco ReflDCo = emptyUniqSet
+ go_dco (GReflRightDCo co) = go_co co
+ go_dco (GReflLeftDCo co) = go_co co
+ go_dco (TyConAppDCo args) = go_dcos args
+ go_dco (AppDCo co arg) = go_dco co `unionUniqSets` go_dco arg
+ go_dco (ForAllDCo _ kind_dco co) = go_dco kind_dco `unionUniqSets` go_dco co
+ go_dco (AxiomInstDCo ax) = go_ax ax
+ go_dco StepsDCo{} = emptyUniqSet
+ go_dco (CoVarDCo {}) = emptyUniqSet
+ go_dco (TransDCo co1 co2) = go_dco co1 `unionUniqSets` go_dco co2
+ go_dco (DehydrateCo co) = go_co co
+ go_dco (UnivDCo p rhs) = go_prov go_dco p `unionUniqSets` go rhs
+ go_dco (SubDCo dco) = go_dco dco
+
+ go_prov get_tycons (PhantomProv co) = get_tycons co
+ go_prov get_tycons (ProofIrrelProv co) = get_tycons co
+ go_prov _ (PluginProv _) = emptyUniqSet
+ go_prov _ (CorePrepProv _) = emptyUniqSet
-- this last case can happen from the tyConsOfType used from
-- checkTauTvUpdate
go_cos cos = foldr (unionUniqSets . go_co) emptyUniqSet cos
+ go_dcos dcos = foldr (unionUniqSets . go_dco) emptyUniqSet dcos
go_tc tc = unitUniqSet tc
go_ax ax = go_tc $ coAxiomTyCon ax
@@ -1318,7 +1425,11 @@ occCheckExpand vs_to_avoid ty
go_co cxt (AxiomInstCo ax ind args) = do { args' <- mapM (go_co cxt) args
; return (AxiomInstCo ax ind args') }
- go_co cxt (UnivCo p r ty1 ty2) = do { p' <- go_prov cxt p
+ go_co ctx (HydrateDCo r ty1 dco ty2)= do { ty1' <- go ctx ty1
+ ; dco' <- go_dco ctx dco
+ ; ty2' <- go ctx ty2
+ ; return (HydrateDCo r ty1' dco' ty2') }
+ go_co cxt (UnivCo p r ty1 ty2) = do { p' <- go_prov (go_co cxt) p
; ty1' <- go cxt ty1
; ty2' <- go cxt ty2
; return (UnivCo p' r ty1' ty2') }
@@ -1342,8 +1453,38 @@ occCheckExpand vs_to_avoid ty
; return (AxiomRuleCo ax cs') }
------------------
- go_prov cxt (PhantomProv co) = PhantomProv <$> go_co cxt co
- go_prov cxt (ProofIrrelProv co) = ProofIrrelProv <$> go_co cxt co
- go_prov _ p@(PluginProv _) = return p
- go_prov _ p@(CorePrepProv _) = return p
+ go_dco _ ReflDCo = pure ReflDCo
+ go_dco cxt (GReflRightDCo co) = GReflRightDCo <$> go_co cxt co
+ go_dco cxt (GReflLeftDCo co) = GReflLeftDCo <$> go_co cxt co
+ go_dco cxt (TyConAppDCo args) = do { args' <- mapM (go_dco cxt) args
+ ; return (TyConAppDCo args') }
+ go_dco cxt (AppDCo co arg) = do { co' <- go_dco cxt co
+ ; arg' <- go_dco cxt arg
+ ; return (AppDCo co' arg') }
+ go_dco cxt@(as, env) (ForAllDCo tv kind_dco body_co)
+ = do { kind_dco' <- go_dco cxt kind_dco
+ ; tv_kind' <- go cxt (varType tv)
+ ; let tv' = setVarType tv $ tv_kind'
+ env' = extendVarEnv env tv tv'
+ as' = as `delVarSet` tv
+ ; body' <- go_dco (as', env') body_co
+ ; return (ForAllDCo tv' kind_dco' body') }
+ go_dco (as,env) co@(CoVarDCo c)
+ | Just c' <- lookupVarEnv env c = return (CoVarDCo c')
+ | bad_var_occ as c = Nothing
+ | otherwise = return co
+
+ go_dco _ dco@AxiomInstDCo{} = pure dco
+ go_dco _ dco@StepsDCo{} = pure dco
+ go_dco cxt (TransDCo co1 co2) = do { co1' <- go_dco cxt co1
+ ; co2' <- go_dco cxt co2
+ ; return (TransDCo co1' co2') }
+ go_dco ctx (UnivDCo prov rhs) = UnivDCo <$> go_prov (go_dco ctx) prov <*> go ctx rhs
+ go_dco ctx (SubDCo dco) = SubDCo <$> go_dco ctx dco
+ go_dco cxt (DehydrateCo co) = DehydrateCo <$> go_co cxt co
+ ------------------
+ go_prov prov_go (PhantomProv co) = PhantomProv <$> prov_go co
+ go_prov prov_go (ProofIrrelProv co) = ProofIrrelProv <$> prov_go co
+ go_prov _ p@(PluginProv _) = return p
+ go_prov _ p@(CorePrepProv _) = return p
diff --git a/compiler/GHC/Core/TyCo/Ppr.hs b/compiler/GHC/Core/TyCo/Ppr.hs
index ce97294a94..2c0838f586 100644
--- a/compiler/GHC/Core/TyCo/Ppr.hs
+++ b/compiler/GHC/Core/TyCo/Ppr.hs
@@ -20,6 +20,7 @@ module GHC.Core.TyCo.Ppr
-- * Pretty-printing coercions
pprCo, pprParendCo,
+ pprDCo,
debugPprType,
) where
@@ -28,7 +29,8 @@ import GHC.Prelude
import {-# SOURCE #-} GHC.CoreToIface
( toIfaceTypeX, toIfaceTyLit, toIfaceForAllBndrs
- , toIfaceTyCon, toIfaceTcArgs, toIfaceCoercionX )
+ , toIfaceTyCon, toIfaceTcArgs
+ , toIfaceCoercionX, toIfaceDCoercionX )
import {-# SOURCE #-} GHC.Core.DataCon
( dataConFullSig , dataConUserTyVarBinders, DataCon )
@@ -126,6 +128,9 @@ pprCo, pprParendCo :: Coercion -> SDoc
pprCo co = getPprStyle $ \ sty -> pprIfaceCoercion (tidyToIfaceCoSty co sty)
pprParendCo co = getPprStyle $ \ sty -> pprParendIfaceCoercion (tidyToIfaceCoSty co sty)
+pprDCo :: DCoercion -> SDoc
+pprDCo co = getPprStyle $ \ sty -> pprIfaceDCoercion (tidyToIfaceDCoSty co sty)
+
tidyToIfaceCoSty :: Coercion -> PprStyle -> IfaceCoercion
tidyToIfaceCoSty co sty
| userStyle sty = tidyToIfaceCo co
@@ -143,6 +148,27 @@ tidyToIfaceCo co = toIfaceCoercionX (mkVarSet free_tcvs) (tidyCo env co)
where
env = tidyFreeTyCoVars emptyTidyEnv free_tcvs
free_tcvs = scopedSort $ tyCoVarsOfCoList co
+
+tidyToIfaceDCoSty :: DCoercion -> PprStyle -> IfaceDCoercion
+tidyToIfaceDCoSty co sty
+ | userStyle sty = tidyToIfaceDCo co
+ | otherwise = toIfaceDCoercionX (tyCoVarsOfDCo co) co
+ -- in latter case, don't tidy, as we'll be printing uniques.
+
+tidyToIfaceDCo :: DCoercion -> IfaceDCoercion
+-- It's vital to tidy before converting to an IfaceType
+-- or nested binders will become indistinguishable!
+--
+-- Also for the free type variables, tell toIfaceDCoercionX to
+-- leave them as IfaceFreeCoVarDCo. This is super-important
+-- for debug printing.
+tidyToIfaceDCo co = toIfaceDCoercionX (mkVarSet free_tcvs) (tidyDCo env co)
+ where
+ env = tidyFreeTyCoVars emptyTidyEnv free_tcvs
+ free_tcvs = scopedSort $ tyCoVarsOfDCoList co
+
+
+
------------
pprClassPred :: Class -> [Type] -> SDoc
pprClassPred clas tys = pprTypeApp (classTyCon clas) tys
diff --git a/compiler/GHC/Core/TyCo/Ppr.hs-boot b/compiler/GHC/Core/TyCo/Ppr.hs-boot
index c031db2f9a..0c32fbfbc8 100644
--- a/compiler/GHC/Core/TyCo/Ppr.hs-boot
+++ b/compiler/GHC/Core/TyCo/Ppr.hs-boot
@@ -1,12 +1,14 @@
module GHC.Core.TyCo.Ppr where
import {-# SOURCE #-} GHC.Types.Var ( TyVar )
-import {-# SOURCE #-} GHC.Core.TyCo.Rep (Type, Kind, Coercion, TyLit)
+import {-# SOURCE #-} GHC.Core.TyCo.Rep (Type, Kind, Coercion, DCoercion, TyLit)
import GHC.Utils.Outputable ( SDoc )
pprType :: Type -> SDoc
debugPprType :: Type -> SDoc
pprKind :: Kind -> SDoc
pprCo :: Coercion -> SDoc
+pprParendCo :: Coercion -> SDoc
+pprDCo :: DCoercion -> SDoc
pprTyLit :: TyLit -> SDoc
pprTyVar :: TyVar -> SDoc
diff --git a/compiler/GHC/Core/TyCo/Rep.hs b/compiler/GHC/Core/TyCo/Rep.hs
index 245a1c507b..11e4b6db79 100644
--- a/compiler/GHC/Core/TyCo/Rep.hs
+++ b/compiler/GHC/Core/TyCo/Rep.hs
@@ -1,5 +1,6 @@
{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE GADTs #-}
{-# OPTIONS_HADDOCK not-home #-}
@@ -42,6 +43,9 @@ module GHC.Core.TyCo.Rep (
CoercionN, CoercionR, CoercionP, KindCoercion,
MCoercion(..), MCoercionR, MCoercionN,
+ DCoercion(..), DCoercionN, KindDCoercion,
+ CoOrDCo(..),
+
-- * Functions over types
mkNakedTyConTy, mkTyVarTy, mkTyVarTys,
mkTyCoVarTy, mkTyCoVarTys,
@@ -61,7 +65,7 @@ module GHC.Core.TyCo.Rep (
TyCoFolder(..), foldTyCo, noView,
-- * Sizes
- typeSize, typesSize, coercionSize, provSize,
+ typeSize, typesSize, coercionSize, dcoercionSize, provSize,
-- * Multiplicities
Scaled(..), scaledMult, scaledThing, mapScaledType, Mult
@@ -69,7 +73,7 @@ module GHC.Core.TyCo.Rep (
import GHC.Prelude
-import {-# SOURCE #-} GHC.Core.TyCo.Ppr ( pprType, pprCo, pprTyLit )
+import {-# SOURCE #-} GHC.Core.TyCo.Ppr ( pprType, pprCo, pprParendCo, pprDCo, pprTyLit )
import {-# SOURCE #-} GHC.Builtin.Types
import {-# SOURCE #-} GHC.Core.Type( chooseFunTyFlag, typeKind, typeTypeOrConstraint )
@@ -882,9 +886,23 @@ data Coercion
-- The number coercions should match exactly the expectations
-- of the CoAxiomRule (i.e., the rule is fully saturated).
- | UnivCo UnivCoProvenance Role Type Type
+ | UnivCo (UnivCoProvenance Coercion) Role Type Type
-- :: _ -> "e" -> _ -> _ -> e
+ -- | Embed a directed coercion into a coercion, by specifying
+ -- the LHS type and role of the directed coercion.
+ --
+ -- The RHS type is also cached, as we often already know the RHS,
+ -- which avoids us computing it anew using 'followDCo'.
+ --
+ -- See Note [Directed coercions]
+ | HydrateDCo
+ Role -- ^ `r`
+ Type -- ^ `lhs`: LHS type of the directed coercion
+ DCoercion
+ Type -- ^ Cached RHS type of the directed coercion.
+ -- Can be computed from `r` and `lhs` using 'followDCo'.
+
| SymCo Coercion -- :: e -> e
| TransCo Coercion Coercion -- :: e -> e -> e
@@ -980,7 +998,7 @@ type MCoercionN = MCoercion
instance Outputable MCoercion where
ppr MRefl = text "MRefl"
- ppr (MCo co) = text "MCo" <+> ppr co
+ ppr (MCo co) = text "MCo" <+> pprParendCo co
{- Note [Refl invariant]
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1402,6 +1420,152 @@ A more nuanced treatment might be able to relax this condition
somewhat, by checking if t1 and/or t2 use their bound variables
in nominal ways. If not, having w be representational is OK.
+Note [Directed coercions]
+~~~~~~~~~~~~~~~~~~~~~~~~~
+A directed coercion is a compact representation of a coercion, used to avoid
+storing a large amount of extra types in coercions in the rewriter.
+
+Recall that a coercion always contains enough information for us to be
+able to retrieve its role and its left and right hand side types.
+
+Examples:
+
+ Refl ty
+ coercionRole: Nominal
+ coercionLKind: ty
+ coercionRKind: ty
+
+ TyConAppCo r tc cos
+ coercionRole: r
+ coercionLKind: mkTyConApp tc (map coercionLKind cos)
+ coercionRKind: mkTyConApp tc (map coercionRKind cos)
+
+In practice, this means that when rewriting type family applications,
+coercions end up storing large amounts of extra information:
+
+ type family a + b where
+ Zero + b = b
+ Succ a + b = Succ (a + b)
+
+Reducing 5 + 0 gives rise to a coercion of the form
+
+ +[1] <Succ (Succ (Succ (Succ Zero)))> <Zero>
+ ; (Succ (+[1] <Succ (Succ (Succ Zero))> <Zero>
+ ; (Succ (+[1] <Succ (Succ Zero)> <Zero>
+ ; (Succ (+[1] <Succ Zero> <Zero>
+ ; (Succ (+[1] <Zero> <Zero>
+ ; (Succ (+[0] <Zero>))))))))))
+
+Compare this to the corresponding directed coercion, where we don't store
+so many types:
+
+ Steps 1
+ ; (TyConApp (Steps 1
+ ; (TyConApp (Steps 1
+ ; (TyConApp (Steps 1
+ ; (TyConApp (Steps 1
+ ; (TyConApp (Steps 1))))))))))
+
+To achieve this, we sacrifice being able to query what the LHS type of a
+directed coercion is. Instead, this information must be provided as an
+input. More specifically, when we have:
+
+ dco :: lhs ~r rhs
+
+We understand that the role `r` and the LHS type `lhs` are **inputs**,
+from which we are able to compute the RHS type `rhs`.
+(This is what the function followDCo does.)
+This allows us to get away with storing less information in a directed
+coercion than in an undirected coercion, while still retaining the ability
+to run Core Lint on our program.
+-}
+
+type DCoercionN = DCoercion
+type KindDCoercion = DCoercionN
+
+-- | A directed coercion is a more compact representation of a coercion,
+-- which is used in the rewriter to avoid producing quadratically large
+-- coercions.
+--
+-- For a directed coercion @dco :: lhs ~r rhs@, we think of the role @r@
+-- and the LHS type @lhs@ as /inputs/. Only once this context is given
+-- are we able to compute the RHS type @rhs@.
+--
+-- See Note [Directed coercions].
+data DCoercion
+
+ -- | 'ReflCo' for 'DCoercion'.
+ = ReflDCo
+
+ -- | 'GReflCo' for 'DCoercion'.
+ | GReflRightDCo CoercionN
+
+ -- | @GReflLeftDCo mco@ corresponds to @SymCo (GReflCo mco)@.
+ --
+ -- We need this alongside @GReflRightDCo@ because we don't have
+ -- symmetry for directed coercions.
+ | GReflLeftDCo CoercionN
+ -- SLD TODO: remove GReflLeftDCo? We could use @GReflRightDCo (mkSymMCo mco)@.
+
+ -- | 'TyConAppCo' for 'DCoercion'.
+ --
+ -- NB: we use 'TyConAppDCo' for functions too,
+ -- unlike coercions which have 'TyConAppCo' and 'FunCo'.
+ | TyConAppDCo [DCoercion]
+
+ -- | 'AppCo' for 'DCoercion'.
+ | AppDCo DCoercion DCoercionN
+
+ -- | 'ForAllCo' for 'DCoercion'.
+ | ForAllDCo TyCoVar KindDCoercion DCoercion
+
+ -- | 'CoVarCo' for 'DCoercion'.
+ | CoVarDCo CoVar
+
+ -- | 'AxiomInstCo' for 'DCoercion', but specialised
+ -- to open type family coercion axioms.
+ --
+ -- For newtypes and closed type families, we use the more
+ -- compact 'StepsDCo'.
+ | AxiomInstDCo (CoAxiom Branched)
+
+ -- | @StepsDCo n@ means: \"take n successive reduction steps\",
+ -- where a reduction step could be using a closed type family equation
+ -- or using a newtype axiom.
+ | StepsDCo !Int
+
+ -- | 'UnivCo' for 'DCoercion'. We only need to store the RHS type,
+ -- as the LHS type and role will be provided by context.
+ | UnivDCo
+ (UnivCoProvenance DCoercion)
+ Type -- ^ RHS type
+
+ -- | 'TransCo' for 'DCoercion'.
+ | TransDCo DCoercion DCoercion
+
+ -- | 'SubCo' for 'DCoercion'.
+ | SubDCo DCoercion
+
+ -- | Embed a coercion inside a directed coercion, e.g. \"forget\"
+ -- that we can compute its LHS type and role without context.
+ | DehydrateCo Coercion
+
+ deriving Data.Data
+
+instance Outputable DCoercion where
+ ppr = pprDCo
+
+-- | A convenient GADT for handling 'Coercion' and 'DCoercion'
+-- at the same time.
+data CoOrDCo co_or_dco where
+ Co :: CoOrDCo Coercion
+ DCo :: CoOrDCo DCoercion
+
+instance Outputable (CoOrDCo co_or_dco) where
+ ppr Co = text "Co"
+ ppr DCo = text "DCo"
+
+{-
%************************************************************************
%* *
@@ -1425,25 +1589,25 @@ role and kind, which is done in the UnivCo constructor.
-- It is reasonable to consider each constructor of 'UnivCoProvenance'
-- as a totally independent coercion form; their only commonality is
-- that they don't tell you what types they coercion between. (That info
--- is in the 'UnivCo' constructor of 'Coercion'.
-data UnivCoProvenance
- = PhantomProv KindCoercion -- ^ See Note [Phantom coercions]. Only in Phantom
- -- roled coercions
+-- is in the 'UnivCo' constructor of 'Coercion').
+data UnivCoProvenance kco
+ = PhantomProv kco -- ^ See Note [Phantom coercions]. Only in Phantom
+ -- roled coercions
- | ProofIrrelProv KindCoercion -- ^ From the fact that any two coercions are
- -- considered equivalent. See Note [ProofIrrelProv].
- -- Can be used in Nominal or Representational coercions
+ | ProofIrrelProv kco -- ^ From the fact that any two coercions are
+ -- considered equivalent. See Note [ProofIrrelProv].
+ -- Can be used in Nominal or Representational coercions
| PluginProv String -- ^ From a plugin, which asserts that this coercion
-- is sound. The string is for the use of the plugin.
- | CorePrepProv -- See Note [Unsafe coercions] in GHC.Core.CoreToStg.Prep
+ | CorePrepProv -- ^ See Note [Unsafe coercions] in GHC.Core.CoreToStg.Prep
Bool -- True <=> the UnivCo must be homogeneously kinded
-- False <=> allow hetero-kinded, e.g. Int ~ Int#
deriving Data.Data
-instance Outputable UnivCoProvenance where
+instance Outputable (UnivCoProvenance kco) where
ppr (PhantomProv _) = text "(phantom)"
ppr (ProofIrrelProv _) = text "(proof irrel.)"
ppr (PluginProv str) = parens (text "plugin" <+> brackets (text str))
@@ -1708,14 +1872,17 @@ data TyCoFolder env a
}
{-# INLINE foldTyCo #-} -- See Note [Specialising foldType]
-foldTyCo :: Monoid a => TyCoFolder env a -> env
- -> (Type -> a, [Type] -> a, Coercion -> a, [Coercion] -> a)
+foldTyCo :: forall env a. Monoid a => TyCoFolder env a -> env
+ -> ( Type -> a, [Type] -> a
+ , Coercion -> a, [Coercion] -> a
+ , DCoercion -> a, [DCoercion] -> a
+ )
foldTyCo (TyCoFolder { tcf_view = view
, tcf_tyvar = tyvar
, tcf_tycobinder = tycobinder
, tcf_covar = covar
, tcf_hole = cohole }) env
- = (go_ty env, go_tys env, go_co env, go_cos env)
+ = (go_ty env, go_tys env, go_co env, go_cos env, go_dco env, go_dcos env)
where
go_ty env ty | Just ty' <- view ty = go_ty env ty'
go_ty env (TyVarTy tv) = tyvar env tv
@@ -1738,38 +1905,61 @@ foldTyCo (TyCoFolder { tcf_view = view
go_cos _ [] = mempty
go_cos env (c:cs) = go_co env c `mappend` go_cos env cs
- go_co env (Refl ty) = go_ty env ty
- go_co env (GRefl _ ty MRefl) = go_ty env ty
- go_co env (GRefl _ ty (MCo co)) = go_ty env ty `mappend` go_co env co
- go_co env (TyConAppCo _ _ args) = go_cos env args
- go_co env (AppCo c1 c2) = go_co env c1 `mappend` go_co env c2
- go_co env (CoVarCo cv) = covar env cv
- go_co env (AxiomInstCo _ _ args) = go_cos env args
- go_co env (HoleCo hole) = cohole env hole
- go_co env (UnivCo p _ t1 t2) = go_prov env p `mappend` go_ty env t1
- `mappend` go_ty env t2
- go_co env (SymCo co) = go_co env co
- go_co env (TransCo c1 c2) = go_co env c1 `mappend` go_co env c2
- go_co env (AxiomRuleCo _ cos) = go_cos env cos
- go_co env (SelCo _ co) = go_co env co
- go_co env (LRCo _ co) = go_co env co
- go_co env (InstCo co arg) = go_co env co `mappend` go_co env arg
- go_co env (KindCo co) = go_co env co
- go_co env (SubCo co) = go_co env co
-
- go_co env (FunCo { fco_mult = cw, fco_arg = c1, fco_res = c2 })
- = go_co env cw `mappend` go_co env c1 `mappend` go_co env c2
-
- go_co env (ForAllCo tv kind_co co)
- = go_co env kind_co `mappend` go_ty env (varType tv)
- `mappend` go_co env' co
+ go_co env (Refl ty) = go_ty env ty
+ go_co env (GRefl _ ty MRefl) = go_ty env ty
+ go_co env (GRefl _ ty (MCo co)) = go_ty env ty `mappend` go_co env co
+ go_co env (TyConAppCo _ _ args) = go_cos env args
+ go_co env (AppCo c1 c2) = go_co env c1 `mappend` go_co env c2
+ go_co env (FunCo _ _ _ cw c1 c2) = go_co env cw `mappend`
+ go_co env c1 `mappend`
+ go_co env c2
+ go_co env (CoVarCo cv) = covar env cv
+ go_co env (AxiomInstCo _ _ args) = go_cos env args
+ go_co env (HoleCo hole) = cohole env hole
+ go_co env (HydrateDCo _ t1 dco _t2) = go_ty env t1 `mappend` go_dco env dco
+ go_co env (UnivCo p _ t1 t2) = go_prov go_co env p
+ `mappend` go_ty env t1
+ `mappend` go_ty env t2
+ go_co env (SymCo co) = go_co env co
+ go_co env (TransCo co1 co2) = go_co env co1 `mappend` go_co env co2
+ go_co env (AxiomRuleCo _ cos) = go_cos env cos
+ go_co env (SelCo _ co) = go_co env co
+ go_co env (LRCo _ co) = go_co env co
+ go_co env (InstCo co arg) = go_co env co `mappend` go_co env arg
+ go_co env (KindCo co) = go_co env co
+ go_co env (SubCo co) = go_co env co
+ go_co env (ForAllCo tv kind_co co) = go_co env kind_co
+ `mappend` go_ty env (varType tv)
+ `mappend` go_co env' co
+ where
+ !env' = tycobinder env tv Inferred
+
+ go_dcos _ [] = mempty
+ go_dcos env (c:cs) = go_dco env c `mappend` go_dcos env cs
+
+ go_dco _ ReflDCo = mempty
+ go_dco env (GReflRightDCo co) = go_co env co
+ go_dco env (GReflLeftDCo co) = go_co env co
+ go_dco env (TyConAppDCo args) = go_dcos env args
+ go_dco env (AppDCo c1 c2) = go_dco env c1 `mappend` go_dco env c2
+ go_dco env (CoVarDCo cv) = covar env cv
+ go_dco _ AxiomInstDCo{} = mempty
+ go_dco _ StepsDCo{} = mempty
+ go_dco env (TransDCo co1 co2) = go_dco env co1 `mappend` go_dco env co2
+ go_dco env (SubDCo dco) = go_dco env dco
+ go_dco env (DehydrateCo co) = go_co env co
+ go_dco env (ForAllDCo tv kind_dco co) = go_dco env kind_dco
+ `mappend` go_ty env (varType tv)
+ `mappend` go_dco env' co
where
- env' = tycobinder env tv Inferred
+ !env' = tycobinder env tv Inferred
+ go_dco env (UnivDCo prov t2) = go_prov go_dco env prov `mappend` go_ty env t2
- go_prov env (PhantomProv co) = go_co env co
- go_prov env (ProofIrrelProv co) = go_co env co
- go_prov _ (PluginProv _) = mempty
- go_prov _ (CorePrepProv _) = mempty
+ go_prov :: (env ->co -> a) -> env -> UnivCoProvenance co -> a
+ go_prov do_fold env (PhantomProv co) = do_fold env co
+ go_prov do_fold env (ProofIrrelProv co) = do_fold env co
+ go_prov _ _ (PluginProv _) = mempty
+ go_prov _ _ (CorePrepProv _) = mempty
-- | A view function that looks through nothing.
noView :: Type -> Maybe Type
@@ -1821,7 +2011,8 @@ coercionSize (FunCo _ _ _ w c1 c2) = 1 + coercionSize c1 + coercionSize c2
coercionSize (CoVarCo _) = 1
coercionSize (HoleCo _) = 1
coercionSize (AxiomInstCo _ _ args) = 1 + sum (map coercionSize args)
-coercionSize (UnivCo p _ t1 t2) = 1 + provSize p + typeSize t1 + typeSize t2
+coercionSize (HydrateDCo _ t1 dco t2) = 1 + typeSize t1 + dcoercionSize dco + typeSize t2
+coercionSize (UnivCo p _ t1 t2) = 1 + provSize coercionSize p + typeSize t1 + typeSize t2
coercionSize (SymCo co) = 1 + coercionSize co
coercionSize (TransCo co1 co2) = 1 + coercionSize co1 + coercionSize co2
coercionSize (SelCo _ co) = 1 + coercionSize co
@@ -1831,11 +2022,26 @@ coercionSize (KindCo co) = 1 + coercionSize co
coercionSize (SubCo co) = 1 + coercionSize co
coercionSize (AxiomRuleCo _ cs) = 1 + sum (map coercionSize cs)
-provSize :: UnivCoProvenance -> Int
-provSize (PhantomProv co) = 1 + coercionSize co
-provSize (ProofIrrelProv co) = 1 + coercionSize co
-provSize (PluginProv _) = 1
-provSize (CorePrepProv _) = 1
+dcoercionSize :: DCoercion -> Int
+dcoercionSize ReflDCo = 1
+dcoercionSize (GReflRightDCo co) = 1 + coercionSize co
+dcoercionSize (GReflLeftDCo co) = 1 + coercionSize co
+dcoercionSize (TyConAppDCo args) = 1 + sum (map dcoercionSize args)
+dcoercionSize (AppDCo co arg) = dcoercionSize co + dcoercionSize arg
+dcoercionSize (ForAllDCo _ h co) = 1 + dcoercionSize co + dcoercionSize h
+dcoercionSize (CoVarDCo _) = 1
+dcoercionSize AxiomInstDCo{} = 1
+dcoercionSize StepsDCo{} = 1
+dcoercionSize (TransDCo co1 co2) = 1 + dcoercionSize co1 + dcoercionSize co2
+dcoercionSize (SubDCo co) = 1 + dcoercionSize co
+dcoercionSize (DehydrateCo co) = 1 + coercionSize co
+dcoercionSize (UnivDCo prov rhs) = 1 + provSize dcoercionSize prov + typeSize rhs
+
+provSize :: (co -> Int) -> UnivCoProvenance co -> Int
+provSize co_size (PhantomProv co) = 1 + co_size co
+provSize co_size (ProofIrrelProv co) = 1 + co_size co
+provSize _ (PluginProv _) = 1
+provSize _ (CorePrepProv _) = 1
{-
************************************************************************
diff --git a/compiler/GHC/Core/TyCo/Rep.hs-boot b/compiler/GHC/Core/TyCo/Rep.hs-boot
index 9b82d3cfa5..714b6371f1 100644
--- a/compiler/GHC/Core/TyCo/Rep.hs-boot
+++ b/compiler/GHC/Core/TyCo/Rep.hs-boot
@@ -9,8 +9,9 @@ import {-# SOURCE #-} GHC.Core.TyCon ( TyCon )
data Type
data Coercion
data FunSel
+data DCoercion
data CoSel
-data UnivCoProvenance
+data UnivCoProvenance co
data TyLit
data MCoercion
diff --git a/compiler/GHC/Core/TyCo/Subst.hs b/compiler/GHC/Core/TyCo/Subst.hs
index 4224bd127b..ee25e16f75 100644
--- a/compiler/GHC/Core/TyCo/Subst.hs
+++ b/compiler/GHC/Core/TyCo/Subst.hs
@@ -6,6 +6,7 @@ Type and Coercion - friends' interface
{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE GADTs #-}
-- | Substitution into types and coercions.
module GHC.Core.TyCo.Subst
@@ -35,17 +36,19 @@ module GHC.Core.TyCo.Subst
substTyUnchecked, substTysUnchecked, substScaledTysUnchecked, substThetaUnchecked,
substTyWithUnchecked, substScaledTyUnchecked,
substCoUnchecked, substCoWithUnchecked,
+ substDCoUnchecked,
substTyWithInScope,
substTys, substScaledTys, substTheta,
lookupTyVar,
substCo, substCos, substCoVar, substCoVars, lookupCoVar,
+ substDCo,
cloneTyVarBndr, cloneTyVarBndrs,
substVarBndr, substVarBndrs,
substTyVarBndr, substTyVarBndrs,
substCoVarBndr,
substTyVar, substTyVars, substTyVarToTyVar,
substTyCoVars,
- substTyCoBndr, substForAllCoBndr,
+ substTyCoBndr, substForAllDCoBndr, substForAllCoBndr,
substVarBndrUsing, substForAllCoBndrUsing,
checkValidSubst, isValidTCvSubst,
) where
@@ -61,7 +64,11 @@ import {-# SOURCE #-} GHC.Core.Coercion
, mkAxiomInstCo, mkAppCo, mkGReflCo
, mkInstCo, mkLRCo, mkTyConAppCo
, mkCoercionType
- , coercionKind, coercionLKind, coVarKindsTypesRole )
+ , mkTyConAppDCo
+ , mkAppDCo, mkForAllDCo, mkReflDCo, mkTransDCo
+ , mkGReflRightDCo, mkGReflLeftDCo
+ , mkHydrateDCo, mkDehydrateCo, mkUnivDCo
+ , coercionKind, coercionLKind, coVarKindsTypesRole)
import {-# SOURCE #-} GHC.Core.TyCo.Ppr ( pprTyVar )
import {-# SOURCE #-} GHC.Core.Ppr ( )
import {-# SOURCE #-} GHC.Core ( CoreExpr )
@@ -682,8 +689,8 @@ isValidTCvSubst (Subst in_scope _ tenv cenv) =
-- | This checks if the substitution satisfies the invariant from
-- Note [The substitution invariant].
-checkValidSubst :: HasDebugCallStack => Subst -> [Type] -> [Coercion] -> a -> a
-checkValidSubst subst@(Subst in_scope _ tenv cenv) tys cos a
+checkValidSubst :: HasDebugCallStack => Subst -> [Type] -> [Coercion] -> [DCoercion] -> a -> a
+checkValidSubst subst@(Subst in_scope _ tenv cenv) tys cos dcos a
= assertPpr (isValidTCvSubst subst)
(text "in_scope" <+> ppr in_scope $$
text "tenv" <+> ppr tenv $$
@@ -691,13 +698,15 @@ checkValidSubst subst@(Subst in_scope _ tenv cenv) tys cos a
text "cenv" <+> ppr cenv $$
text "cenvFVs" <+> ppr (shallowTyCoVarsOfCoVarEnv cenv) $$
text "tys" <+> ppr tys $$
- text "cos" <+> ppr cos) $
+ text "cos" <+> ppr cos $$
+ text "dcos" <+> ppr dcos) $
assertPpr tysCosFVsInScope
(text "in_scope" <+> ppr in_scope $$
text "tenv" <+> ppr tenv $$
text "cenv" <+> ppr cenv $$
text "tys" <+> ppr tys $$
text "cos" <+> ppr cos $$
+ text "dcos" <+> ppr dcos $$
text "needInScope" <+> ppr needInScope)
a
where
@@ -705,7 +714,8 @@ checkValidSubst subst@(Subst in_scope _ tenv cenv) tys cos a
-- It's OK to use nonDetKeysUFM here, because we only use this list to
-- remove some elements from a set
needInScope = (shallowTyCoVarsOfTypes tys `unionVarSet`
- shallowTyCoVarsOfCos cos)
+ shallowTyCoVarsOfCos cos `unionVarSet`
+ shallowTyCoVarsOfDCos dcos)
`delListFromUniqSet_Directly` substDomain
tysCosFVsInScope = needInScope `varSetInScope` in_scope
@@ -716,7 +726,7 @@ checkValidSubst subst@(Subst in_scope _ tenv cenv) tys cos a
substTy :: HasDebugCallStack => Subst -> Type -> Type
substTy subst ty
| isEmptyTCvSubst subst = ty
- | otherwise = checkValidSubst subst [ty] [] $
+ | otherwise = checkValidSubst subst [ty] [] [] $
subst_ty subst ty
-- | Substitute within a 'Type' disabling the sanity checks.
@@ -741,12 +751,12 @@ substScaledTyUnchecked subst scaled_ty = mapScaledType (substTyUnchecked subst)
substTys :: HasDebugCallStack => Subst -> [Type] -> [Type]
substTys subst tys
| isEmptyTCvSubst subst = tys
- | otherwise = checkValidSubst subst tys [] $ map (subst_ty subst) tys
+ | otherwise = checkValidSubst subst tys [] [] $ map (subst_ty subst) tys
substScaledTys :: HasDebugCallStack => Subst -> [Scaled Type] -> [Scaled Type]
substScaledTys subst scaled_tys
| isEmptyTCvSubst subst = scaled_tys
- | otherwise = checkValidSubst subst (map scaledMult scaled_tys ++ map scaledThing scaled_tys) [] $
+ | otherwise = checkValidSubst subst (map scaledMult scaled_tys ++ map scaledThing scaled_tys) [] [] $
map (mapScaledType (subst_ty subst)) scaled_tys
-- | Substitute within several 'Type's disabling the sanity checks.
@@ -846,13 +856,31 @@ lookupTyVar (Subst _ _ tenv _) tv
= assert (isTyVar tv )
lookupVarEnv tenv tv
+-- | Substitute within a 'DCoercion'
+-- The substitution has to satisfy the invariants described in
+-- Note [The substitution invariant].
+substDCo :: HasDebugCallStack => Subst -> DCoercion -> DCoercion
+substDCo subst dco
+ | isEmptyTCvSubst subst = dco
+ | otherwise = checkValidSubst subst [] [] [dco] $ subst_dco subst dco
+
+-- | Substitute within a 'DCoercion' disabling sanity checks.
+-- The problems that the sanity checks in substCo catch are described in
+-- Note [The substitution invariant].
+-- The goal of #11371 is to migrate all the calls of substDCoUnchecked to
+-- substDCo and remove this function. Please don't use in new code.
+substDCoUnchecked :: Subst -> DCoercion -> DCoercion
+substDCoUnchecked subst co
+ | isEmptyTCvSubst subst = co
+ | otherwise = subst_dco subst co
+
-- | Substitute within a 'Coercion'
-- The substitution has to satisfy the invariants described in
-- Note [The substitution invariant].
substCo :: HasDebugCallStack => Subst -> Coercion -> Coercion
substCo subst co
| isEmptyTCvSubst subst = co
- | otherwise = checkValidSubst subst [] [co] $ subst_co subst co
+ | otherwise = checkValidSubst subst [] [co] [] $ subst_co subst co
-- | Substitute within a 'Coercion' disabling sanity checks.
-- The problems that the sanity checks in substCo catch are described in
@@ -870,18 +898,23 @@ substCoUnchecked subst co
substCos :: HasDebugCallStack => Subst -> [Coercion] -> [Coercion]
substCos subst cos
| isEmptyTCvSubst subst = cos
- | otherwise = checkValidSubst subst [] cos $ map (subst_co subst) cos
+ | otherwise = checkValidSubst subst [] cos [] $ map (subst_co subst) cos
subst_co :: Subst -> Coercion -> Coercion
-subst_co subst co
- = go co
+subst_co = fst . subst_co_dco
+
+subst_dco :: Subst -> DCoercion -> DCoercion
+subst_dco = snd . subst_co_dco
+
+subst_co_dco :: Subst -> (Coercion -> Coercion, DCoercion -> DCoercion)
+subst_co_dco subst = (go, go_dco)
where
go_ty :: Type -> Type
go_ty = subst_ty subst
go_mco :: MCoercion -> MCoercion
go_mco MRefl = MRefl
- go_mco (MCo co) = MCo (go co)
+ go_mco (MCo co) = MCo $! go co
go :: Coercion -> Coercion
go (Refl ty) = mkNomReflCo $! (go_ty ty)
@@ -896,7 +929,10 @@ subst_co subst co
go (FunCo r afl afr w co1 co2) = ((mkFunCo2 r afl afr $! go w) $! go co1) $! go co2
go (CoVarCo cv) = substCoVar subst cv
go (AxiomInstCo con ind cos) = mkAxiomInstCo con ind $! map go cos
- go (UnivCo p r t1 t2) = (((mkUnivCo $! go_prov p) $! r) $!
+ go (HydrateDCo r ty dco rty) = (((mkHydrateDCo $! r) $! go_ty ty) $! go_dco dco) $! go_ty rty
+ -- Here we can either substitute the RHS or recompute it from the rest of the information.
+
+ go (UnivCo p r t1 t2) = (((mkUnivCo $! go_prov go p) $! r) $!
(go_ty t1)) $! (go_ty t2)
go (SymCo co) = mkSymCo $! (go co)
go (TransCo co1 co2) = (mkTransCo $! (go co1)) $! (go co2)
@@ -909,10 +945,30 @@ subst_co subst co
in cs1 `seqList` AxiomRuleCo c cs1
go (HoleCo h) = HoleCo $! go_hole h
- go_prov (PhantomProv kco) = PhantomProv (go kco)
- go_prov (ProofIrrelProv kco) = ProofIrrelProv (go kco)
- go_prov p@(PluginProv _) = p
- go_prov p@(CorePrepProv _) = p
+ go_dco :: DCoercion -> DCoercion
+ go_dco ReflDCo = mkReflDCo
+ go_dco (GReflRightDCo co) = mkGReflRightDCo $! go co
+ go_dco (GReflLeftDCo co) = mkGReflLeftDCo $! go co
+ go_dco (TyConAppDCo args) = let args' = map go_dco args
+ in args' `seqList` mkTyConAppDCo args'
+ go_dco (AppDCo co arg) = (mkAppDCo $! go_dco co) $! go_dco arg
+ go_dco (CoVarDCo cv) = mkDehydrateCo $! substCoVar subst cv
+ go_dco dco@AxiomInstDCo{} = dco
+ go_dco dco@StepsDCo{} = dco
+ go_dco (TransDCo co1 co2) = (mkTransDCo $! go_dco co1) $! go_dco co2
+ go_dco (DehydrateCo co) = mkDehydrateCo $! go co
+ go_dco (ForAllDCo tv kind_dco co)
+ = case substForAllDCoBndrUnchecked subst tv kind_dco of
+ (subst', tv', kind_dco') ->
+ ((mkForAllDCo $! tv') $! kind_dco') $! subst_dco subst' co
+ go_dco (UnivDCo prov rhs) = (mkUnivDCo (go_prov go_dco prov)) $! go_ty rhs
+ go_dco (SubDCo dco) = SubDCo $ go_dco dco
+
+ go_prov :: (co -> co) -> UnivCoProvenance co -> UnivCoProvenance co
+ go_prov do_subst (PhantomProv kco) = PhantomProv $! do_subst kco
+ go_prov do_subst (ProofIrrelProv kco) = ProofIrrelProv $! do_subst kco
+ go_prov _ p@(PluginProv _) = p
+ go_prov _ p@(CorePrepProv _) = p
-- See Note [Substituting in a coercion hole]
go_hole h@(CoercionHole { ch_co_var = cv })
@@ -921,7 +977,12 @@ subst_co subst co
substForAllCoBndr :: Subst -> TyCoVar -> KindCoercion
-> (Subst, TyCoVar, Coercion)
substForAllCoBndr subst
- = substForAllCoBndrUsing False (substCo subst) subst
+ = substForAllCoBndrUsing Co False (substTy subst) (substCo subst) subst
+
+substForAllDCoBndr :: Subst -> TyCoVar -> KindDCoercion
+ -> (Subst, TyCoVar, DCoercion)
+substForAllDCoBndr subst
+ = substForAllCoBndrUsing DCo False (substTy subst) (substDCo subst) subst
-- | Like 'substForAllCoBndr', but disables sanity checks.
-- The problems that the sanity checks in substCo catch are described in
@@ -931,65 +992,94 @@ substForAllCoBndr subst
substForAllCoBndrUnchecked :: Subst -> TyCoVar -> KindCoercion
-> (Subst, TyCoVar, Coercion)
substForAllCoBndrUnchecked subst
- = substForAllCoBndrUsing False (substCoUnchecked subst) subst
+ = substForAllCoBndrUsing Co False (substTyUnchecked subst) (substCoUnchecked subst) subst
+
+substForAllDCoBndrUnchecked :: Subst -> TyCoVar -> KindDCoercion
+ -> (Subst, TyCoVar, DCoercion)
+substForAllDCoBndrUnchecked subst
+ = substForAllCoBndrUsing DCo False (substTyUnchecked subst) (substDCoUnchecked subst) subst
+
-- See Note [Sym and ForAllCo]
-substForAllCoBndrUsing :: Bool -- apply sym to binder?
- -> (Coercion -> Coercion) -- transformation to kind co
- -> Subst -> TyCoVar -> KindCoercion
- -> (Subst, TyCoVar, KindCoercion)
-substForAllCoBndrUsing sym sco subst old_var
- | isTyVar old_var = substForAllCoTyVarBndrUsing sym sco subst old_var
- | otherwise = substForAllCoCoVarBndrUsing sym sco subst old_var
-
-substForAllCoTyVarBndrUsing :: Bool -- apply sym to binder?
- -> (Coercion -> Coercion) -- transformation to kind co
- -> Subst -> TyVar -> KindCoercion
- -> (Subst, TyVar, KindCoercion)
-substForAllCoTyVarBndrUsing sym sco (Subst in_scope idenv tenv cenv) old_var old_kind_co
+substForAllCoBndrUsing :: CoOrDCo kco
+ -> Bool -- apply sym to binder?
+ -> (Type -> Type)
+ -> (kco -> kco) -- transformation to kind co
+ -> Subst -> TyCoVar -> kco
+ -> (Subst, TyCoVar, kco)
+substForAllCoBndrUsing co_or_dco sym sty sco subst old_var
+ | isTyVar old_var = substForAllCoTyVarBndrUsing co_or_dco sym sty sco subst old_var
+ | otherwise = substForAllCoCoVarBndrUsing co_or_dco sym sty sco subst old_var
+
+substForAllCoTyVarBndrUsing :: CoOrDCo kco
+ -> Bool -- apply sym to binder?
+ -> (Type -> Type) -- transformation to types
+ -> (kco -> kco) -- transformation to kind co
+ -> Subst -> TyVar -> kco
+ -> (Subst, TyVar, kco)
+substForAllCoTyVarBndrUsing co_or_dco sym sty sco (Subst in_scope idenv tenv cenv) old_var old_kind_co
= assert (isTyVar old_var )
( Subst (in_scope `extendInScopeSet` new_var) idenv new_env cenv
, new_var, new_kind_co )
where
new_env | no_change && not sym = delVarEnv tenv old_var
| sym = extendVarEnv tenv old_var $
- TyVarTy new_var `CastTy` new_kind_co
+ mk_cast (TyVarTy new_var) new_kind_co
| otherwise = extendVarEnv tenv old_var (TyVarTy new_var)
- no_kind_change = noFreeVarsOfCo old_kind_co
+ no_kind_change = case co_or_dco of
+ Co -> noFreeVarsOfCo old_kind_co
+ DCo -> noFreeVarsOfDCo old_kind_co
+ mk_cast = case co_or_dco of
+ Co -> CastTy
+ DCo -> pprPanic "substForAllCoTyVarBndrUsing DCo Sym"
+ (vcat [ text "kind_co:" <+> ppr old_kind_co ])
+
no_change = no_kind_change && (new_var == old_var)
new_kind_co | no_kind_change = old_kind_co
| otherwise = sco old_kind_co
- new_ki1 = coercionLKind new_kind_co
+ new_ki1 = case co_or_dco of
+ Co -> coercionLKind new_kind_co
-- We could do substitution to (tyVarKind old_var). We don't do so because
-- we already substituted new_kind_co, which contains the kind information
-- we want. We don't want to do substitution once more. Also, in most cases,
-- new_kind_co is a Refl, in which case coercionKind is really fast.
+ DCo -> sty (tyVarKind old_var)
new_var = uniqAway in_scope (setTyVarKind old_var new_ki1)
-substForAllCoCoVarBndrUsing :: Bool -- apply sym to binder?
- -> (Coercion -> Coercion) -- transformation to kind co
- -> Subst -> CoVar -> KindCoercion
- -> (Subst, CoVar, KindCoercion)
-substForAllCoCoVarBndrUsing sym sco (Subst in_scope idenv tenv cenv)
+substForAllCoCoVarBndrUsing :: CoOrDCo kco
+ -> Bool -- apply sym to binder?
+ -> (Type -> Type) -- transformation to types
+ -> (kco -> kco) -- transformation to kind co
+ -> Subst -> CoVar -> kco
+ -> (Subst, CoVar, kco)
+substForAllCoCoVarBndrUsing co_or_dco sym sty sco (Subst in_scope idenv tenv cenv)
old_var old_kind_co
- = assert (isCoVar old_var )
+ = assert (isCoVar old_var)
( Subst (in_scope `extendInScopeSet` new_var) idenv tenv new_cenv
, new_var, new_kind_co )
where
new_cenv | no_change && not sym = delVarEnv cenv old_var
| otherwise = extendVarEnv cenv old_var (mkCoVarCo new_var)
- no_kind_change = noFreeVarsOfCo old_kind_co
+ no_kind_change = case co_or_dco of
+ Co -> noFreeVarsOfCo old_kind_co
+ DCo -> noFreeVarsOfDCo old_kind_co
no_change = no_kind_change && (new_var == old_var)
new_kind_co | no_kind_change = old_kind_co
| otherwise = sco old_kind_co
- Pair h1 h2 = coercionKind new_kind_co
+ Pair h1 h2 = case co_or_dco of
+ Co -> coercionKind new_kind_co
+ DCo ->
+ let l_ty = sty (varType old_var)
+ r_ty = pprPanic "substForAllCoCoVarBndrUsing DCo Sym"
+ (vcat [ text "kind_co:" <+> ppr old_kind_co])
+ in Pair l_ty r_ty
new_var = uniqAway in_scope $ mkCoVar (varName old_var) new_var_type
new_var_type | sym = h2
diff --git a/compiler/GHC/Core/TyCo/Tidy.hs b/compiler/GHC/Core/TyCo/Tidy.hs
index bfd7e4c7cc..7e7bdf433d 100644
--- a/compiler/GHC/Core/TyCo/Tidy.hs
+++ b/compiler/GHC/Core/TyCo/Tidy.hs
@@ -12,6 +12,7 @@ module GHC.Core.TyCo.Tidy
tidyTyCoVarOcc,
tidyTopType,
tidyCo, tidyCos,
+ tidyDCo,
tidyForAllTyBinder, tidyForAllTyBinders
) where
@@ -218,8 +219,13 @@ tidyTopType ty = tidyType emptyTidyEnv ty
--
-- See Note [Strictness in tidyType and friends]
tidyCo :: TidyEnv -> Coercion -> Coercion
-tidyCo env@(_, subst) co
- = go co
+tidyCo = fst . tidyCoDCo
+
+tidyDCo :: TidyEnv -> DCoercion -> DCoercion
+tidyDCo = snd . tidyCoDCo
+
+tidyCoDCo :: TidyEnv -> (Coercion -> Coercion, DCoercion -> DCoercion)
+tidyCoDCo env@(_, subst) = (go, go_dco)
where
go_mco MRefl = MRefl
go_mco (MCo co) = MCo $! go co
@@ -238,7 +244,8 @@ tidyCo env@(_, subst) co
Just cv' -> CoVarCo cv'
go (HoleCo h) = HoleCo h
go (AxiomInstCo con ind cos) = AxiomInstCo con ind $! strictMap go cos
- go (UnivCo p r t1 t2) = (((UnivCo $! (go_prov p)) $! r) $!
+ go (HydrateDCo r t1 dco rty) = ((HydrateDCo r $! tidyType env t1) $! go_dco dco) $! tidyType env rty
+ go (UnivCo p r t1 t2) = (((UnivCo $! (go_prov go p)) $! r) $!
tidyType env t1) $! tidyType env t2
go (SymCo co) = SymCo $! go co
go (TransCo co1 co2) = (TransCo $! go co1) $! go co2
@@ -249,10 +256,30 @@ tidyCo env@(_, subst) co
go (SubCo co) = SubCo $! go co
go (AxiomRuleCo ax cos) = AxiomRuleCo ax $ strictMap go cos
- go_prov (PhantomProv co) = PhantomProv $! go co
- go_prov (ProofIrrelProv co) = ProofIrrelProv $! go co
- go_prov p@(PluginProv _) = p
- go_prov p@(CorePrepProv _) = p
+ go_dco ReflDCo = ReflDCo
+ go_dco (GReflRightDCo co) = GReflRightDCo $! go co
+ go_dco (GReflLeftDCo co) = GReflLeftDCo $! go co
+ go_dco (TyConAppDCo cos) = TyConAppDCo $! strictMap go_dco cos
+ go_dco (AppDCo co1 co2) = (AppDCo $! go_dco co1) $! go_dco co2
+ go_dco (ForAllDCo tv h co) = ((ForAllDCo $! tvp) $! (go_dco h)) $! tidyDCo envp co
+ where (envp, tvp) = tidyVarBndr env tv
+ -- the case above duplicates a bit of work in tidying h and the kind
+ -- of tv. But the alternative is to use coercionKind, which seems worse.
+ go_dco (CoVarDCo cv) = case lookupVarEnv subst cv of
+ Nothing -> CoVarDCo cv
+ Just cv' -> CoVarDCo cv'
+ go_dco dco@AxiomInstDCo{} = dco
+ go_dco dco@StepsDCo{} = dco
+ go_dco (TransDCo co1 co2) = (TransDCo $! go_dco co1) $! go_dco co2
+ go_dco (DehydrateCo co) = DehydrateCo $! go co
+ go_dco (SubDCo dco) = SubDCo $! go_dco dco
+ go_dco (UnivDCo prov rhs) = (UnivDCo $! go_prov go_dco prov) $! tidyType env rhs
+
+ go_prov :: (co -> co) -> UnivCoProvenance co -> UnivCoProvenance co
+ go_prov do_tidy (PhantomProv co) = PhantomProv $! do_tidy co
+ go_prov do_tidy (ProofIrrelProv co) = ProofIrrelProv $! do_tidy co
+ go_prov _ p@(PluginProv _) = p
+ go_prov _ p@(CorePrepProv _) = p
tidyCos :: TidyEnv -> [Coercion] -> [Coercion]
tidyCos env = strictMap (tidyCo env)
diff --git a/compiler/GHC/Core/Type.hs b/compiler/GHC/Core/Type.hs
index a5dc5a6865..51147f5d60 100644
--- a/compiler/GHC/Core/Type.hs
+++ b/compiler/GHC/Core/Type.hs
@@ -38,7 +38,6 @@ module GHC.Core.Type (
funTyConAppTy_maybe, funTyFlagTyCon,
tyConAppFunTy_maybe, tyConAppFunCo_maybe,
mkFunctionType, mkScaledFunctionTys, chooseFunTyFlag,
-
mkTyConApp, mkTyConTy,
tyConAppTyCon_maybe, tyConAppTyConPicky_maybe,
tyConAppArgs_maybe, tyConAppTyCon, tyConAppArgs,
@@ -276,8 +275,16 @@ import {-# SOURCE #-} GHC.Core.Coercion
, mkSymCo, mkTransCo, mkSelCo, mkLRCo, mkInstCo
, mkKindCo, mkSubCo, mkFunCo, funRole
, decomposePiCos, coercionKind
- , coercionRKind, coercionType
+ , mkTyConAppDCo, mkAppDCo
+ , mkForAllDCo
+ , mkTransDCo
+ , mkReflDCo, mkGReflRightDCo, mkGReflLeftDCo
+ , mkDehydrateCo
+ , mkHydrateDCo
+ , decomposePiCos
+ , coercionKind, coercionRKind, coercionType
, isReflexiveCo, seqCo
+ , mkUnivDCo
, topNormaliseNewType_maybe
)
import {-# SOURCE #-} GHC.Tc.Utils.TcType ( isConcreteTyVar )
@@ -557,8 +564,10 @@ expandTypeSynonyms ty
= substCoVar subst cv
go_co subst (AxiomInstCo ax ind args)
= mkAxiomInstCo ax ind (map (go_co subst) args)
+ go_co subst (HydrateDCo r t1 dco t2)
+ = mkHydrateDCo r (go subst t1) (go_dco subst dco) (go subst t2)
go_co subst (UnivCo p r t1 t2)
- = mkUnivCo (go_prov subst p) r (go subst t1) (go subst t2)
+ = mkUnivCo (go_prov (go_co subst) p) r (go subst t1) (go subst t2)
go_co subst (SymCo co)
= mkSymCo (go_co subst co)
go_co subst (TransCo co1 co2)
@@ -578,16 +587,42 @@ expandTypeSynonyms ty
go_co _ (HoleCo h)
= pprPanic "expandTypeSynonyms hit a hole" (ppr h)
- go_prov subst (PhantomProv co) = PhantomProv (go_co subst co)
- go_prov subst (ProofIrrelProv co) = ProofIrrelProv (go_co subst co)
- go_prov _ p@(PluginProv _) = p
- go_prov _ p@(CorePrepProv _) = p
+ go_dco _ ReflDCo
+ = mkReflDCo
+ go_dco subst (GReflRightDCo co)
+ = mkGReflRightDCo (go_co subst co)
+ go_dco subst (GReflLeftDCo co)
+ = mkGReflLeftDCo (go_co subst co)
+ go_dco subst (TyConAppDCo args)
+ = mkTyConAppDCo (map (go_dco subst) args)
+ go_dco subst (AppDCo co arg)
+ = mkAppDCo (go_dco subst co) (go_dco subst arg)
+ go_dco subst (ForAllDCo tv kind_dco co)
+ = let (subst', tv', kind_dco') = go_dcobndr subst tv kind_dco in
+ mkForAllDCo tv' kind_dco' (go_dco subst' co)
+ go_dco subst (CoVarDCo cv)
+ = mkDehydrateCo (substCoVar subst cv)
+ go_dco _ dco@AxiomInstDCo{}
+ = dco
+ go_dco _ dco@StepsDCo{}
+ = dco
+ go_dco subst (TransDCo co1 co2)
+ = mkTransDCo (go_dco subst co1) (go_dco subst co2)
+ go_dco subst (DehydrateCo co) = mkDehydrateCo (go_co subst co)
+ go_dco subst (UnivDCo p rhs) = mkUnivDCo (go_prov (go_dco subst) p) (go subst rhs)
+ go_dco subst (SubDCo dco) = SubDCo (go_dco subst dco)
+
+ go_prov do_subst (PhantomProv co) = PhantomProv $ do_subst co
+ go_prov do_subst (ProofIrrelProv co) = ProofIrrelProv $ do_subst co
+ go_prov _ p@(PluginProv _) = p
+ go_prov _ p@(CorePrepProv _) = p
-- the "False" and "const" are to accommodate the type of
-- substForAllCoBndrUsing, which is general enough to
-- handle coercion optimization (which sometimes swaps the
-- order of a coercion)
- go_cobndr subst = substForAllCoBndrUsing False (go_co subst) subst
+ go_cobndr subst = substForAllCoBndrUsing Co False (go subst) (go_co subst) subst
+ go_dcobndr subst = substForAllCoBndrUsing DCo False (go subst) (go_dco subst) subst
{- Notes on type synonyms
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -960,24 +995,26 @@ mapTyCoX (TyCoMapper { tcm_tyvar = tyvar
go_mco _ MRefl = return MRefl
go_mco env (MCo co) = MCo <$> (go_co env co)
- go_co env (Refl ty) = Refl <$> go_ty env ty
- go_co env (GRefl r ty mco) = mkGReflCo r <$> go_ty env ty <*> go_mco env mco
- go_co env (AppCo c1 c2) = mkAppCo <$> go_co env c1 <*> go_co env c2
+ go_co env (Refl ty) = Refl <$> go_ty env ty
+ go_co env (GRefl r ty mco) = mkGReflCo r <$> go_ty env ty <*> go_mco env mco
+ go_co env (AppCo c1 c2) = mkAppCo <$> go_co env c1 <*> go_co env c2
go_co env (FunCo r afl afr cw c1 c2) = mkFunCo2 r afl afr <$> go_co env cw
<*> go_co env c1 <*> go_co env c2
- go_co env (CoVarCo cv) = covar env cv
- go_co env (HoleCo hole) = cohole env hole
- go_co env (UnivCo p r t1 t2) = mkUnivCo <$> go_prov env p <*> pure r
- <*> go_ty env t1 <*> go_ty env t2
- go_co env (SymCo co) = mkSymCo <$> go_co env co
- go_co env (TransCo c1 c2) = mkTransCo <$> go_co env c1 <*> go_co env c2
- go_co env (AxiomRuleCo r cos) = AxiomRuleCo r <$> go_cos env cos
- go_co env (SelCo i co) = mkSelCo i <$> go_co env co
- go_co env (LRCo lr co) = mkLRCo lr <$> go_co env co
- go_co env (InstCo co arg) = mkInstCo <$> go_co env co <*> go_co env arg
- go_co env (KindCo co) = mkKindCo <$> go_co env co
- go_co env (SubCo co) = mkSubCo <$> go_co env co
- go_co env (AxiomInstCo ax i cos) = mkAxiomInstCo ax i <$> go_cos env cos
+ go_co env (CoVarCo cv) = covar env cv
+ go_co env (HoleCo hole) = cohole env hole
+ go_co env (HydrateDCo r t1 dco t2) = mkHydrateDCo r <$> go_ty env t1 <*> go_dco env dco <*> go_ty env t2
+ go_co env (UnivCo p r t1 t2) = mkUnivCo <$> go_prov (go_co env) p <*> pure r
+ <*> go_ty env t1 <*> go_ty env t2
+ go_co env (SymCo co) = mkSymCo <$> go_co env co
+ go_co env (TransCo c1 c2) = mkTransCo <$> go_co env c1 <*> go_co env c2
+ go_co env (AxiomRuleCo r cos) = AxiomRuleCo r <$> go_cos env cos
+ go_co env (SelCo i co) = mkSelCo i <$> go_co env co
+ go_co env (LRCo lr co) = mkLRCo lr <$> go_co env co
+ go_co env (InstCo co arg) = mkInstCo <$> go_co env co <*> go_co env arg
+ go_co env (KindCo co) = mkKindCo <$> go_co env co
+ go_co env (SubCo co) = mkSubCo <$> go_co env co
+ go_co env (AxiomInstCo ax i cos) = mkAxiomInstCo ax i <$> go_cos env cos
+
go_co env co@(TyConAppCo r tc cos)
| isTcTyCon tc
= do { tc' <- tycon tc
@@ -996,10 +1033,38 @@ mapTyCoX (TyCoMapper { tcm_tyvar = tyvar
; return $ mkForAllCo tv' kind_co' co' }
-- See Note [Efficiency for ForAllCo case of mapTyCoX]
- go_prov env (PhantomProv co) = PhantomProv <$> go_co env co
- go_prov env (ProofIrrelProv co) = ProofIrrelProv <$> go_co env co
- go_prov _ p@(PluginProv _) = return p
- go_prov _ p@(CorePrepProv _) = return p
+ go_dcos _ [] = return []
+ go_dcos env (co:cos) = (:) <$> go_dco env co <*> go_dcos env cos
+
+ go_dco _ ReflDCo = pure mkReflDCo
+ go_dco env (GReflRightDCo co) = mkGReflRightDCo <$> go_co env co
+ go_dco env (GReflLeftDCo co) = mkGReflLeftDCo <$> go_co env co
+ go_dco env (AppDCo c1 c2) = mkAppDCo <$> go_dco env c1 <*> go_dco env c2
+ go_dco env (CoVarDCo cv) = mkDehydrateCo <$> covar env cv
+ go_dco env (TransDCo c1 c2) = mkTransDCo <$> go_dco env c1 <*> go_dco env c2
+ go_dco _ dco@AxiomInstDCo{} = pure dco
+ go_dco _ dco@StepsDCo{} = pure dco
+ go_dco env (DehydrateCo co) = mkDehydrateCo <$> go_co env co
+ go_dco env co@(TyConAppDCo cos)
+ -- Not a TcTyCon
+ | null cos -- Avoid allocation in this very
+ = return co -- common case (E.g. Int, LiftedRep etc)
+
+ | otherwise
+ = mkTyConAppDCo <$> go_dcos env cos
+ go_dco env (ForAllDCo tv kind_dco co)
+ = do { kind_dco' <- go_dco env kind_dco
+ ; (env', tv') <- tycobinder env tv Inferred
+ ; co' <- go_dco env' co
+ ; return $ mkForAllDCo tv' kind_dco' co' }
+ -- See Note [Efficiency for ForAllCo case of mapTyCoX]
+ go_dco env (UnivDCo p rhs) = mkUnivDCo <$> go_prov (go_dco env) p <*> go_ty env rhs
+ go_dco env (SubDCo dco) = SubDCo <$> go_dco env dco
+
+ go_prov go (PhantomProv co) = PhantomProv <$> go co
+ go_prov go (ProofIrrelProv co) = ProofIrrelProv <$> go co
+ go_prov _ p@(PluginProv _) = return p
+ go_prov _ p@(CorePrepProv _) = return p
{- *********************************************************************
diff --git a/compiler/GHC/Core/Unify.hs-boot b/compiler/GHC/Core/Unify.hs-boot
new file mode 100644
index 0000000000..b08fcf83c9
--- /dev/null
+++ b/compiler/GHC/Core/Unify.hs-boot
@@ -0,0 +1,9 @@
+module GHC.Core.Unify where
+
+import GHC.Core.TyCo.Subst (Subst)
+import GHC.Core.Type (Type)
+
+
+import Data.Maybe (Maybe)
+
+tcMatchTys :: [Type] -> [Type] -> Maybe Subst
diff --git a/compiler/GHC/Core/Utils.hs b/compiler/GHC/Core/Utils.hs
index 565bf698bc..020b956d8f 100644
--- a/compiler/GHC/Core/Utils.hs
+++ b/compiler/GHC/Core/Utils.hs
@@ -2385,9 +2385,9 @@ isEmptyTy ty
normSplitTyConApp_maybe :: FamInstEnvs -> Type -> Maybe (TyCon, [Type], Coercion)
normSplitTyConApp_maybe fam_envs ty
| let Reduction co ty1 = topNormaliseType_maybe fam_envs ty
- `orElse` (mkReflRedn Representational ty)
+ `orElse` (mkReflRedn ty)
, Just (tc, tc_args) <- splitTyConApp_maybe ty1
- = Just (tc, tc_args, co)
+ = Just (tc, tc_args, mkHydrateDCo Representational ty co ty1)
normSplitTyConApp_maybe _ _ = Nothing
{-
diff --git a/compiler/GHC/CoreToIface.hs b/compiler/GHC/CoreToIface.hs
index e6d3fe93b7..9de8e84972 100644
--- a/compiler/GHC/CoreToIface.hs
+++ b/compiler/GHC/CoreToIface.hs
@@ -24,6 +24,7 @@ module GHC.CoreToIface
, tidyToIfaceTcArgs
-- * Coercions
, toIfaceCoercion, toIfaceCoercionX
+ , toIfaceDCoercionX
-- * Pattern synonyms
, patSynToIfaceDecl
-- * Expressions
@@ -275,10 +276,14 @@ toIfaceCoercion :: Coercion -> IfaceCoercion
toIfaceCoercion = toIfaceCoercionX emptyVarSet
toIfaceCoercionX :: VarSet -> Coercion -> IfaceCoercion
+toIfaceDCoercionX :: VarSet -> DCoercion -> IfaceDCoercion
-- (toIfaceCoercionX free ty)
-- translates the tyvars in 'free' as IfaceFreeTyVars
-toIfaceCoercionX fr co
- = go co
+toIfaceCoercionX = fst . toIfaceCoercionDCoercion
+toIfaceDCoercionX = snd . toIfaceCoercionDCoercion
+
+toIfaceCoercionDCoercion :: VarSet -> (Coercion -> IfaceCoercion, DCoercion -> IfaceDCoercion)
+toIfaceCoercionDCoercion fr = (go, go_dco)
where
go_mco MRefl = IfaceMRefl
go_mco (MCo co) = IfaceMCo $ go co
@@ -301,7 +306,8 @@ toIfaceCoercionX fr co
go (SubCo co) = IfaceSubCo (go co)
go (AxiomRuleCo co cs) = IfaceAxiomRuleCo (coaxrName co) (map go cs)
go (AxiomInstCo c i cs) = IfaceAxiomInstCo (coAxiomName c) i (map go cs)
- go (UnivCo p r t1 t2) = IfaceUnivCo (go_prov p) r
+ go (HydrateDCo r t dco _)= IfaceHydrateDCo r (toIfaceTypeX fr t) (go_dco dco)
+ go (UnivCo p r t1 t2) = IfaceUnivCo (go_prov go p) r
(toIfaceTypeX fr t1)
(toIfaceTypeX fr t2)
go co@(TyConAppCo r tc cos)
@@ -317,11 +323,34 @@ toIfaceCoercionX fr co
where
fr' = fr `delVarSet` tv
- go_prov :: UnivCoProvenance -> IfaceUnivCoProv
- go_prov (PhantomProv co) = IfacePhantomProv (go co)
- go_prov (ProofIrrelProv co) = IfaceProofIrrelProv (go co)
- go_prov (PluginProv str) = IfacePluginProv str
- go_prov (CorePrepProv b) = IfaceCorePrepProv b
+
+ go_dco ReflDCo = IfaceReflDCo
+ go_dco (GReflRightDCo co) = IfaceGReflRightDCo (go co)
+ go_dco (GReflLeftDCo co) = IfaceGReflLeftDCo (go co)
+ go_dco (CoVarDCo cv)
+ -- See [TcTyVars in IfaceType] in GHC.Iface.Type
+ | cv `elemVarSet` fr = IfaceFreeCoVarDCo cv
+ | otherwise = IfaceCoVarDCo (toIfaceCoVar cv)
+ go_dco (AppDCo co1 co2) = IfaceAppDCo (go_dco co1) (go_dco co2)
+ go_dco (TransDCo co1 co2) = IfaceTransDCo (go_dco co1) (go_dco co2)
+ go_dco (AxiomInstDCo ax) = IfaceAxiomInstDCo (coAxiomName ax)
+ go_dco (StepsDCo n) = IfaceStepsDCo n
+ go_dco (TyConAppDCo cos) = IfaceTyConAppDCo (map go_dco cos)
+ go_dco (SubDCo dco) = IfaceSubDCo (go_dco dco)
+ go_dco (DehydrateCo co) = IfaceDehydrateCo (go co)
+ go_dco (ForAllDCo tv k co) = IfaceForAllDCo (toIfaceBndr tv)
+ (toIfaceDCoercionX fr' k)
+ (toIfaceDCoercionX fr' co)
+ where
+ fr' = fr `delVarSet` tv
+
+ go_dco (UnivDCo p rhs) = IfaceUnivDCo (go_prov go_dco p) (toIfaceTypeX fr rhs)
+
+ go_prov :: (co -> iface_co) -> UnivCoProvenance co -> IfaceUnivCoProv iface_co
+ go_prov to_iface (PhantomProv co) = IfacePhantomProv (to_iface co)
+ go_prov to_iface (ProofIrrelProv co) = IfaceProofIrrelProv (to_iface co)
+ go_prov _ (PluginProv str) = IfacePluginProv str
+ go_prov _ (CorePrepProv b) = IfaceCorePrepProv b
toIfaceTcArgs :: TyCon -> [Type] -> IfaceAppArgs
toIfaceTcArgs = toIfaceTcArgsX emptyVarSet
diff --git a/compiler/GHC/CoreToIface.hs-boot b/compiler/GHC/CoreToIface.hs-boot
index 61b291f324..fbd7436651 100644
--- a/compiler/GHC/CoreToIface.hs-boot
+++ b/compiler/GHC/CoreToIface.hs-boot
@@ -1,8 +1,9 @@
module GHC.CoreToIface where
-import {-# SOURCE #-} GHC.Core.TyCo.Rep ( Type, TyLit, Coercion )
+import {-# SOURCE #-} GHC.Core.TyCo.Rep ( Type, TyLit, Coercion, DCoercion )
import {-# SOURCE #-} GHC.Iface.Type( IfaceType, IfaceTyCon, IfaceBndr
- , IfaceCoercion, IfaceTyLit, IfaceAppArgs )
+ , IfaceCoercion, IfaceDCoercion
+ , IfaceTyLit, IfaceAppArgs )
import GHC.Types.Var ( VarBndr, TyCoVar )
import GHC.Types.Var.Env ( TidyEnv )
import GHC.Core.TyCon ( TyCon )
@@ -15,4 +16,5 @@ toIfaceForAllBndrs :: [VarBndr TyCoVar flag] -> [VarBndr IfaceBndr flag]
toIfaceTyCon :: TyCon -> IfaceTyCon
toIfaceTcArgs :: TyCon -> [Type] -> IfaceAppArgs
toIfaceCoercionX :: VarSet -> Coercion -> IfaceCoercion
+toIfaceDCoercionX :: VarSet -> DCoercion -> IfaceDCoercion
tidyToIfaceTcArgs :: TidyEnv -> TyCon -> [Type] -> IfaceAppArgs
diff --git a/compiler/GHC/Driver/Config.hs b/compiler/GHC/Driver/Config.hs
index c92da902c9..b63a36335d 100644
--- a/compiler/GHC/Driver/Config.hs
+++ b/compiler/GHC/Driver/Config.hs
@@ -16,7 +16,15 @@ import GHCi.Message (EvalOpts(..))
-- | Initialise coercion optimiser configuration from DynFlags
initOptCoercionOpts :: DynFlags -> OptCoercionOpts
initOptCoercionOpts dflags = OptCoercionOpts
- { optCoercionEnabled = not (hasNoOptCoercion dflags)
+ { optCoercionOpts
+ = if hasNoOptCoercion dflags
+ then Nothing
+ else
+ let dco_method =
+ if hasKeepDCoercions dflags
+ then OptDCos { skipDCoOpt = True }
+ else HydrateDCos
+ in Just dco_method
}
-- | Initialise Simple optimiser configuration from DynFlags
diff --git a/compiler/GHC/Driver/Flags.hs b/compiler/GHC/Driver/Flags.hs
index 2099d7c100..813a723be0 100644
--- a/compiler/GHC/Driver/Flags.hs
+++ b/compiler/GHC/Driver/Flags.hs
@@ -472,6 +472,7 @@ data GeneralFlag
| Opt_G_NoStateHack
| Opt_G_NoOptCoercion
+ | Opt_G_KeepDCoercions
deriving (Eq, Show, Enum)
-- Check whether a flag should be considered an "optimisation flag"
diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs
index 84962f7868..150384670d 100644
--- a/compiler/GHC/Driver/Session.hs
+++ b/compiler/GHC/Driver/Session.hs
@@ -28,7 +28,8 @@ module GHC.Driver.Session (
FatalMessager, FlushOut(..),
ProfAuto(..),
glasgowExtsFlags,
- hasPprDebug, hasNoDebugOutput, hasNoStateHack, hasNoOptCoercion,
+ hasPprDebug, hasNoDebugOutput, hasNoStateHack,
+ hasNoOptCoercion, hasKeepDCoercions,
dopt, dopt_set, dopt_unset,
gopt, gopt_set, gopt_unset, setGeneralFlag', unSetGeneralFlag',
wopt, wopt_set, wopt_unset,
@@ -1467,6 +1468,8 @@ hasNoStateHack = gopt Opt_G_NoStateHack
hasNoOptCoercion :: DynFlags -> Bool
hasNoOptCoercion = gopt Opt_G_NoOptCoercion
+hasKeepDCoercions :: DynFlags -> Bool
+hasKeepDCoercions = gopt Opt_G_KeepDCoercions
-- | Test whether a 'DumpFlag' is set
dopt :: DumpFlag -> DynFlags -> Bool
@@ -2384,6 +2387,8 @@ dynamic_flags_deps = [
(NoArg (setGeneralFlag Opt_G_NoStateHack))
, make_ord_flag defGhcFlag "fno-opt-coercion"
(NoArg (setGeneralFlag Opt_G_NoOptCoercion))
+ , make_ord_flag defGhcFlag "fkeep-dcoercions"
+ (NoArg (setGeneralFlag Opt_G_KeepDCoercions))
, make_ord_flag defGhcFlag "with-rtsopts"
(HasArg setRtsOpts)
, make_ord_flag defGhcFlag "rtsopts"
@@ -4086,6 +4091,7 @@ optLevelFlags -- see Note [Documenting optimisation flags]
, ([0], Opt_IgnoreInterfacePragmas)
, ([0], Opt_OmitInterfacePragmas)
+ , ([0], Opt_G_KeepDCoercions)
, ([1,2], Opt_CoreConstantFolding)
diff --git a/compiler/GHC/Iface/Rename.hs b/compiler/GHC/Iface/Rename.hs
index b372e7a1d9..b8e0992a3a 100644
--- a/compiler/GHC/Iface/Rename.hs
+++ b/compiler/GHC/Iface/Rename.hs
@@ -675,12 +675,14 @@ rnIfaceCo (IfaceCoVarCo lcl) = IfaceCoVarCo <$> pure lcl
rnIfaceCo (IfaceHoleCo lcl) = IfaceHoleCo <$> pure lcl
rnIfaceCo (IfaceAxiomInstCo n i cs)
= IfaceAxiomInstCo <$> rnIfaceGlobal n <*> pure i <*> mapM rnIfaceCo cs
+rnIfaceCo (IfaceHydrateDCo r t1 dco)
+ = IfaceHydrateDCo r <$> rnIfaceType t1 <*> rnIfaceDCo dco
rnIfaceCo (IfaceUnivCo s r t1 t2)
- = IfaceUnivCo s r <$> rnIfaceType t1 <*> rnIfaceType t2
+ = IfaceUnivCo <$> rnIfaceProv rnIfaceCo s <*> pure r <*> rnIfaceType t1 <*> rnIfaceType t2
rnIfaceCo (IfaceSymCo c)
= IfaceSymCo <$> rnIfaceCo c
-rnIfaceCo (IfaceTransCo c1 c2)
- = IfaceTransCo <$> rnIfaceCo c1 <*> rnIfaceCo c2
+rnIfaceCo (IfaceTransCo co1 co2)
+ = IfaceTransCo <$> rnIfaceCo co1 <*> rnIfaceCo co2
rnIfaceCo (IfaceInstCo c1 c2)
= IfaceInstCo <$> rnIfaceCo c1 <*> rnIfaceCo c2
rnIfaceCo (IfaceSelCo d c) = IfaceSelCo d <$> rnIfaceCo c
@@ -690,6 +692,42 @@ rnIfaceCo (IfaceAxiomRuleCo ax cos)
= IfaceAxiomRuleCo ax <$> mapM rnIfaceCo cos
rnIfaceCo (IfaceKindCo c) = IfaceKindCo <$> rnIfaceCo c
+rnIfaceDCo :: Rename IfaceDCoercion
+rnIfaceDCo IfaceReflDCo
+ = return IfaceReflDCo
+rnIfaceDCo (IfaceGReflRightDCo co)
+ = IfaceGReflRightDCo <$> rnIfaceCo co
+rnIfaceDCo (IfaceGReflLeftDCo co)
+ = IfaceGReflLeftDCo <$> rnIfaceCo co
+rnIfaceDCo (IfaceTyConAppDCo dcos)
+ = IfaceTyConAppDCo <$> mapM rnIfaceDCo dcos
+rnIfaceDCo (IfaceAppDCo dco1 dco2)
+ = IfaceAppDCo <$> rnIfaceDCo dco1 <*> rnIfaceDCo dco2
+rnIfaceDCo (IfaceForAllDCo bndr dco1 dco2)
+ = IfaceForAllDCo <$> rnIfaceBndr bndr <*> rnIfaceDCo dco1 <*> rnIfaceDCo dco2
+rnIfaceDCo (IfaceCoVarDCo lcl)
+ = return (IfaceCoVarDCo lcl)
+rnIfaceDCo (IfaceFreeCoVarDCo c)
+ = return (IfaceFreeCoVarDCo c)
+rnIfaceDCo (IfaceAxiomInstDCo ax)
+ = return (IfaceAxiomInstDCo ax)
+rnIfaceDCo (IfaceStepsDCo steps)
+ = return (IfaceStepsDCo steps)
+rnIfaceDCo (IfaceTransDCo co1 co2)
+ = IfaceTransDCo <$> rnIfaceDCo co1 <*> rnIfaceDCo co2
+rnIfaceDCo (IfaceDehydrateCo co)
+ = IfaceDehydrateCo <$> rnIfaceCo co
+rnIfaceDCo (IfaceUnivDCo prov rhs)
+ = IfaceUnivDCo <$> rnIfaceProv rnIfaceDCo prov <*> rnIfaceType rhs
+rnIfaceDCo (IfaceSubDCo dco)
+ = IfaceSubDCo <$> rnIfaceDCo dco
+
+rnIfaceProv :: Rename iface_co -> Rename (IfaceUnivCoProv iface_co)
+rnIfaceProv rn_thing (IfacePhantomProv iface_co) = IfacePhantomProv <$> rn_thing iface_co
+rnIfaceProv rn_thing (IfaceProofIrrelProv iface_co) = IfaceProofIrrelProv <$> rn_thing iface_co
+rnIfaceProv _ (IfacePluginProv str) = return (IfacePluginProv str)
+rnIfaceProv _ (IfaceCorePrepProv homo) = return (IfaceCorePrepProv homo)
+
rnIfaceTyCon :: Rename IfaceTyCon
rnIfaceTyCon (IfaceTyCon n info)
= IfaceTyCon <$> rnIfaceGlobal n <*> pure info
diff --git a/compiler/GHC/Iface/Syntax.hs b/compiler/GHC/Iface/Syntax.hs
index 71b87cb19c..686cb187cf 100644
--- a/compiler/GHC/Iface/Syntax.hs
+++ b/compiler/GHC/Iface/Syntax.hs
@@ -1724,8 +1724,10 @@ freeNamesIfCoercion (IfaceCoVarCo _) = emptyNameSet
freeNamesIfCoercion (IfaceHoleCo _) = emptyNameSet
freeNamesIfCoercion (IfaceAxiomInstCo ax _ cos)
= unitNameSet ax &&& fnList freeNamesIfCoercion cos
+freeNamesIfCoercion (IfaceHydrateDCo _ t1 dco)
+ = freeNamesIfType t1 &&& freeNamesIfDCoercion dco
freeNamesIfCoercion (IfaceUnivCo p _ t1 t2)
- = freeNamesIfProv p &&& freeNamesIfType t1 &&& freeNamesIfType t2
+ = freeNamesIfProv freeNamesIfCoercion p &&& freeNamesIfType t1 &&& freeNamesIfType t2
freeNamesIfCoercion (IfaceSymCo c)
= freeNamesIfCoercion c
freeNamesIfCoercion (IfaceTransCo c1 c2)
@@ -1744,11 +1746,33 @@ freeNamesIfCoercion (IfaceAxiomRuleCo _ax cos)
-- the axiom is just a string, so we don't count it as a name.
= fnList freeNamesIfCoercion cos
-freeNamesIfProv :: IfaceUnivCoProv -> NameSet
-freeNamesIfProv (IfacePhantomProv co) = freeNamesIfCoercion co
-freeNamesIfProv (IfaceProofIrrelProv co) = freeNamesIfCoercion co
-freeNamesIfProv (IfacePluginProv _) = emptyNameSet
-freeNamesIfProv (IfaceCorePrepProv _) = emptyNameSet
+freeNamesIfDCoercion :: IfaceDCoercion -> NameSet
+freeNamesIfDCoercion IfaceReflDCo = emptyNameSet
+freeNamesIfDCoercion (IfaceGReflRightDCo co)
+ = freeNamesIfCoercion co
+freeNamesIfDCoercion (IfaceGReflLeftDCo co)
+ = freeNamesIfCoercion co
+freeNamesIfDCoercion (IfaceTyConAppDCo cos)
+ = fnList freeNamesIfDCoercion cos
+freeNamesIfDCoercion (IfaceAppDCo c1 c2)
+ = freeNamesIfDCoercion c1 &&& freeNamesIfDCoercion c2
+freeNamesIfDCoercion (IfaceForAllDCo _ kind_co co)
+ = freeNamesIfDCoercion kind_co &&& freeNamesIfDCoercion co
+freeNamesIfDCoercion (IfaceFreeCoVarDCo _) = emptyNameSet
+freeNamesIfDCoercion (IfaceCoVarDCo _) = emptyNameSet
+freeNamesIfDCoercion (IfaceAxiomInstDCo ax) = unitNameSet ax
+freeNamesIfDCoercion IfaceStepsDCo{} = emptyNameSet
+freeNamesIfDCoercion (IfaceTransDCo co1 co2)
+ = freeNamesIfDCoercion co1 &&& freeNamesIfDCoercion co2
+freeNamesIfDCoercion (IfaceDehydrateCo co) = freeNamesIfCoercion co
+freeNamesIfDCoercion (IfaceUnivDCo p rhs) = freeNamesIfProv freeNamesIfDCoercion p &&& freeNamesIfType rhs
+freeNamesIfDCoercion (IfaceSubDCo dco) = freeNamesIfDCoercion dco
+
+freeNamesIfProv :: (co -> NameSet) -> IfaceUnivCoProv co -> NameSet
+freeNamesIfProv free_names (IfacePhantomProv co) = free_names co
+freeNamesIfProv free_names (IfaceProofIrrelProv co) = free_names co
+freeNamesIfProv _ (IfacePluginProv _) = emptyNameSet
+freeNamesIfProv _ (IfaceCorePrepProv _) = emptyNameSet
freeNamesIfVarBndr :: VarBndr IfaceBndr vis -> NameSet
freeNamesIfVarBndr (Bndr bndr _) = freeNamesIfBndr bndr
diff --git a/compiler/GHC/Iface/Type.hs b/compiler/GHC/Iface/Type.hs
index 2b45a712e6..b43c285eac 100644
--- a/compiler/GHC/Iface/Type.hs
+++ b/compiler/GHC/Iface/Type.hs
@@ -19,6 +19,7 @@ module GHC.Iface.Type (
IfaceType(..), IfacePredType, IfaceKind, IfaceCoercion(..),
IfaceMCoercion(..),
+ IfaceDCoercion(..),
IfaceUnivCoProv(..),
IfaceMult,
IfaceTyCon(..),
@@ -53,6 +54,7 @@ module GHC.Iface.Type (
pprIfaceForAllPart, pprIfaceForAllPartMust, pprIfaceForAll,
pprIfaceSigmaType, pprIfaceTyLit,
pprIfaceCoercion, pprParendIfaceCoercion,
+ pprIfaceDCoercion,
splitIfaceSigmaTy, pprIfaceTypeApp, pprUserIfaceForAll,
pprIfaceCoTcApp, pprTyTcApp, pprIfacePrefixApp,
isIfaceRhoType,
@@ -387,7 +389,8 @@ data IfaceCoercion
-- There are only a fixed number of CoAxiomRules, so it suffices
-- to use an IfaceLclName to distinguish them.
-- See Note [Adding built-in type families] in GHC.Builtin.Types.Literals
- | IfaceUnivCo IfaceUnivCoProv Role IfaceType IfaceType
+ | IfaceHydrateDCo Role IfaceType IfaceDCoercion
+ | IfaceUnivCo (IfaceUnivCoProv IfaceCoercion) Role IfaceType IfaceType
| IfaceSymCo IfaceCoercion
| IfaceTransCo IfaceCoercion IfaceCoercion
| IfaceSelCo CoSel IfaceCoercion
@@ -398,11 +401,27 @@ data IfaceCoercion
| IfaceFreeCoVar CoVar -- See Note [Free tyvars in IfaceType]
| IfaceHoleCo CoVar -- ^ See Note [Holes in IfaceCoercion]
-data IfaceUnivCoProv
- = IfacePhantomProv IfaceCoercion
- | IfaceProofIrrelProv IfaceCoercion
+data IfaceDCoercion
+ = IfaceReflDCo
+ | IfaceGReflRightDCo IfaceCoercion
+ | IfaceGReflLeftDCo IfaceCoercion
+ | IfaceTyConAppDCo [IfaceDCoercion]
+ | IfaceAppDCo IfaceDCoercion IfaceDCoercion
+ | IfaceForAllDCo IfaceBndr IfaceDCoercion IfaceDCoercion
+ | IfaceCoVarDCo IfLclName
+ | IfaceFreeCoVarDCo CoVar
+ | IfaceAxiomInstDCo IfExtName
+ | IfaceStepsDCo !Int
+ | IfaceTransDCo IfaceDCoercion IfaceDCoercion
+ | IfaceDehydrateCo IfaceCoercion
+ | IfaceSubDCo IfaceDCoercion
+ | IfaceUnivDCo (IfaceUnivCoProv IfaceDCoercion) IfaceType
+
+data IfaceUnivCoProv iface_co
+ = IfacePhantomProv iface_co
+ | IfaceProofIrrelProv iface_co
| IfacePluginProv String
- | IfaceCorePrepProv Bool -- See defn of CorePrepProv
+ | IfaceCorePrepProv Bool -- See defn of CorePrepProv
{- Note [Holes in IfaceCoercion]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -609,7 +628,8 @@ substIfaceType env ty
go_co (IfaceCoVarCo cv) = IfaceCoVarCo cv
go_co (IfaceHoleCo cv) = IfaceHoleCo cv
go_co (IfaceAxiomInstCo a i cos) = IfaceAxiomInstCo a i (go_cos cos)
- go_co (IfaceUnivCo prov r t1 t2) = IfaceUnivCo (go_prov prov) r (go t1) (go t2)
+ go_co (IfaceHydrateDCo r t1 dco) = IfaceHydrateDCo r (go t1) (go_dco dco)
+ go_co (IfaceUnivCo prov r t1 t2) = IfaceUnivCo (go_prov go_co prov) r (go t1) (go t2)
go_co (IfaceSymCo co) = IfaceSymCo (go_co co)
go_co (IfaceTransCo co1 co2) = IfaceTransCo (go_co co1) (go_co co2)
go_co (IfaceSelCo n co) = IfaceSelCo n (go_co co)
@@ -619,12 +639,28 @@ substIfaceType env ty
go_co (IfaceSubCo co) = IfaceSubCo (go_co co)
go_co (IfaceAxiomRuleCo n cos) = IfaceAxiomRuleCo n (go_cos cos)
+ go_dco IfaceReflDCo = IfaceReflDCo
+ go_dco (IfaceGReflRightDCo co) = IfaceGReflRightDCo (go_co co)
+ go_dco (IfaceGReflLeftDCo co) = IfaceGReflLeftDCo (go_co co)
+ go_dco (IfaceTyConAppDCo cos) = IfaceTyConAppDCo (go_dcos cos)
+ go_dco (IfaceAppDCo c1 c2) = IfaceAppDCo (go_dco c1) (go_dco c2)
+ go_dco (IfaceForAllDCo {}) = pprPanic "substIfaceDCoercion" (ppr ty)
+ go_dco (IfaceFreeCoVarDCo cv) = IfaceFreeCoVarDCo cv
+ go_dco (IfaceCoVarDCo cv) = IfaceCoVarDCo cv
+ go_dco dco@IfaceAxiomInstDCo{} = dco
+ go_dco dco@IfaceStepsDCo{} = dco
+ go_dco (IfaceTransDCo co1 co2) = IfaceTransDCo (go_dco co1) (go_dco co2)
+ go_dco (IfaceDehydrateCo co) = IfaceDehydrateCo (go_co co)
+ go_dco (IfaceSubDCo dco) = IfaceSubDCo (go_dco dco)
+ go_dco (IfaceUnivDCo p rhs) = IfaceUnivDCo (go_prov go_dco p) (go rhs)
+
go_cos = map go_co
+ go_dcos = map go_dco
- go_prov (IfacePhantomProv co) = IfacePhantomProv (go_co co)
- go_prov (IfaceProofIrrelProv co) = IfaceProofIrrelProv (go_co co)
- go_prov co@(IfacePluginProv _) = co
- go_prov co@(IfaceCorePrepProv _) = co
+ go_prov do_subst (IfacePhantomProv co) = IfacePhantomProv (do_subst co)
+ go_prov do_subst (IfaceProofIrrelProv co) = IfaceProofIrrelProv (do_subst co)
+ go_prov _ co@(IfacePluginProv _) = co
+ go_prov _ co@(IfaceCorePrepProv _) = co
substIfaceAppArgs :: IfaceTySubst -> IfaceAppArgs -> IfaceAppArgs
substIfaceAppArgs env args
@@ -1248,9 +1284,9 @@ pprIfaceForAllPartMust :: [IfaceForAllBndr] -> [IfacePredType] -> SDoc -> SDoc
pprIfaceForAllPartMust tvs ctxt sdoc
= ppr_iface_forall_part ShowForAllMust tvs ctxt sdoc
-pprIfaceForAllCoPart :: [(IfLclName, IfaceCoercion)] -> SDoc -> SDoc
-pprIfaceForAllCoPart tvs sdoc
- = sep [ pprIfaceForAllCo tvs, sdoc ]
+pprIfaceForAllCoPart :: (iface_co -> SDoc) -> [(IfLclName, iface_co)] -> SDoc -> SDoc
+pprIfaceForAllCoPart ppr_iface_co tvs sdoc
+ = sep [ pprIfaceForAllCo ppr_iface_co tvs, sdoc ]
ppr_iface_forall_part :: ShowForAllFlag
-> [IfaceForAllBndr] -> [IfacePredType] -> SDoc -> SDoc
@@ -1287,12 +1323,12 @@ ppr_itv_bndrs all_bndrs@(bndr@(Bndr _ vis) : bndrs) vis1
| otherwise = (all_bndrs, [])
ppr_itv_bndrs [] _ = ([], [])
-pprIfaceForAllCo :: [(IfLclName, IfaceCoercion)] -> SDoc
-pprIfaceForAllCo [] = empty
-pprIfaceForAllCo tvs = text "forall" <+> pprIfaceForAllCoBndrs tvs <> dot
+pprIfaceForAllCo :: (iface_co -> SDoc) -> [(IfLclName, iface_co)] -> SDoc
+pprIfaceForAllCo _ [] = empty
+pprIfaceForAllCo ppr_iface_co tvs = text "forall" <+> pprIfaceForAllCoBndrs ppr_iface_co tvs <> dot
-pprIfaceForAllCoBndrs :: [(IfLclName, IfaceCoercion)] -> SDoc
-pprIfaceForAllCoBndrs bndrs = hsep $ map pprIfaceForAllCoBndr bndrs
+pprIfaceForAllCoBndrs :: (iface_co -> SDoc) -> [(IfLclName, iface_co)] -> SDoc
+pprIfaceForAllCoBndrs ppr_iface_co bndrs = hsep $ map (pprIfaceForAllCoBndr ppr_iface_co) bndrs
pprIfaceForAllBndr :: IfaceForAllBndr -> SDoc
pprIfaceForAllBndr bndr =
@@ -1306,9 +1342,9 @@ pprIfaceForAllBndr bndr =
-- See Note [Suppressing binder signatures]
suppress_sig = SuppressBndrSig False
-pprIfaceForAllCoBndr :: (IfLclName, IfaceCoercion) -> SDoc
-pprIfaceForAllCoBndr (tv, kind_co)
- = parens (ppr tv <+> dcolon <+> pprIfaceCoercion kind_co)
+pprIfaceForAllCoBndr :: (iface_co -> SDoc) -> (IfLclName, iface_co) -> SDoc
+pprIfaceForAllCoBndr ppr_iface_co (tv, kind_co)
+ = parens (ppr tv <+> dcolon <+> ppr_iface_co kind_co)
-- | Show forall flag
--
@@ -1793,7 +1829,7 @@ ppr_co ctxt_prec (IfaceAppCo co1 co2)
ppr_co funPrec co1 <+> pprParendIfaceCoercion co2
ppr_co ctxt_prec co@(IfaceForAllCo {})
= maybeParen ctxt_prec funPrec $
- pprIfaceForAllCoPart tvs (pprIfaceCoercion inner_co)
+ pprIfaceForAllCoPart pprIfaceCoercion tvs (pprIfaceCoercion inner_co)
where
(tvs, inner_co) = split_co co
@@ -1808,10 +1844,13 @@ ppr_co _ (IfaceFreeCoVar covar) = ppr covar
ppr_co _ (IfaceCoVarCo covar) = ppr covar
ppr_co _ (IfaceHoleCo covar) = braces (ppr covar)
+ppr_co ctxt_prec (IfaceHydrateDCo role ty1 dco)
+ = maybeParen ctxt_prec appPrec $
+ text "Hydrate" <+> (ppr role <+> ppr_ty appPrec ty1 <+> pprParendIfaceDCoercion dco)
ppr_co _ (IfaceUnivCo prov role ty1 ty2)
= text "Univ" <> (parens $
- sep [ ppr role <+> pprIfaceUnivCoProv prov
- , dcolon <+> ppr ty1 <> comma <+> ppr ty2 ])
+ sep [ ppr role <+> pprIfaceUnivCoProv pprParendIfaceCoercion prov
+ , dcolon <+> ppr_ty appPrec ty1 <+> text "~" <+> ppr_ty appPrec ty2 ])
ppr_co ctxt_prec (IfaceInstCo co ty)
= maybeParen ctxt_prec appPrec $
@@ -1853,14 +1892,77 @@ ppr_role r = underscore <> pp_role
Phantom -> char 'P'
------------------
-pprIfaceUnivCoProv :: IfaceUnivCoProv -> SDoc
-pprIfaceUnivCoProv (IfacePhantomProv co)
- = text "phantom" <+> pprParendIfaceCoercion co
-pprIfaceUnivCoProv (IfaceProofIrrelProv co)
- = text "irrel" <+> pprParendIfaceCoercion co
-pprIfaceUnivCoProv (IfacePluginProv s)
+pprIfaceDCoercion, pprParendIfaceDCoercion :: IfaceDCoercion -> SDoc
+pprIfaceDCoercion = ppr_dco topPrec
+pprParendIfaceDCoercion = ppr_dco appPrec
+
+ppr_dco :: PprPrec -> IfaceDCoercion -> SDoc
+ppr_dco _ IfaceReflDCo = text "Refl"
+ppr_dco ctxt_prec (IfaceGReflRightDCo co)
+ = maybeParen ctxt_prec appPrec $
+ text "GReflRight" <+> pprParendIfaceCoercion co
+ppr_dco ctxt_prec (IfaceGReflLeftDCo co)
+ = maybeParen ctxt_prec appPrec $
+ text "GReflLeft" <+> pprParendIfaceCoercion co
+ppr_dco ctxt_prec (IfaceTyConAppDCo cos)
+ = ppr_special_dco ctxt_prec (text "TyConApp") cos
+ppr_dco ctxt_prec (IfaceAppDCo co1 co2)
+ = maybeParen ctxt_prec appPrec $
+ ppr_dco funPrec co1 <+> pprParendIfaceDCoercion co2
+ppr_dco ctxt_prec co@(IfaceForAllDCo {})
+ = maybeParen ctxt_prec funPrec $
+ pprIfaceForAllCoPart pprIfaceDCoercion tvs (pprIfaceDCoercion inner_dco)
+ where
+ (tvs, inner_dco) = split_dco co
+
+ split_dco (IfaceForAllDCo (IfaceTvBndr (name, _)) kind_dco co')
+ = let (tvs, co'') = split_dco co' in ((name,kind_dco):tvs,co'')
+ split_dco (IfaceForAllDCo (IfaceIdBndr (_, name, _)) kind_dco co')
+ = let (tvs, co'') = split_dco co' in ((name,kind_dco):tvs,co'')
+ split_dco co' = ([], co')
+
+-- Why these two? See Note [Free tyvars in IfaceType]
+ppr_dco _ (IfaceFreeCoVarDCo covar) = ppr covar
+ppr_dco _ (IfaceCoVarDCo covar) = ppr covar
+
+ppr_dco _ (IfaceAxiomInstDCo ax) = ppr ax
+ppr_dco ctxt_prec (IfaceStepsDCo n)
+ = maybeParen ctxt_prec appPrec $
+ text "Steps" <+> ppr n
+
+ppr_dco ctxt_prec (IfaceTransDCo co1 co2)
+ -- chain nested TransCo
+ = let ppr_trans (IfaceTransDCo c1 c2) = semi <+> ppr_dco topPrec c1 : ppr_trans c2
+ ppr_trans c = [semi <+> ppr_dco opPrec c]
+ in maybeParen ctxt_prec opPrec $
+ vcat (ppr_dco topPrec co1 : ppr_trans co2)
+ppr_dco ctxt_prec (IfaceDehydrateCo co)
+ = maybeParen ctxt_prec funPrec
+ $ text "Dehydrate" <+> pprParendIfaceCoercion co
+ppr_dco _ (IfaceUnivDCo prov rhs)
+ = text "UnivDCo" <> (parens $
+ sep [ pprIfaceUnivCoProv pprParendIfaceDCoercion prov
+ , dcolon <+> text "_ ~>" <+> ppr_ty appPrec rhs ])
+ppr_dco ctxt_prec (IfaceSubDCo dco)
+ = maybeParen ctxt_prec appPrec $
+ text "Sub" <+> ppr_dco appPrec dco
+
+-- AMG TODO: deduplicate some of the pretty-printing code
+ppr_special_dco :: PprPrec -> SDoc -> [IfaceDCoercion] -> SDoc
+ppr_special_dco ctxt_prec doc cos
+ = maybeParen ctxt_prec appPrec
+ (sep [doc, nest 4 (sep (map pprParendIfaceDCoercion cos))])
+
+
+------------------
+pprIfaceUnivCoProv :: (iface_co -> SDoc) -> IfaceUnivCoProv iface_co -> SDoc
+pprIfaceUnivCoProv ppr_co (IfacePhantomProv co)
+ = text "phantom" <+> ppr_co co
+pprIfaceUnivCoProv ppr_co (IfaceProofIrrelProv co)
+ = text "irrel" <+> ppr_co co
+pprIfaceUnivCoProv _ (IfacePluginProv s)
= text "plugin" <+> doubleQuotes (text s)
-pprIfaceUnivCoProv (IfaceCorePrepProv _)
+pprIfaceUnivCoProv _ (IfaceCorePrepProv _)
= text "CorePrep"
-------------------
@@ -2154,6 +2256,11 @@ instance Binary IfaceCoercion where
putByte bh 17
put_ bh a
put_ bh b
+ put_ bh (IfaceHydrateDCo r ty dco) = do
+ putByte bh 18
+ put_ bh r
+ put_ bh ty
+ put_ bh dco
put_ _ (IfaceFreeCoVar cv)
= pprPanic "Can't serialise IfaceFreeCoVar" (ppr cv)
put_ _ (IfaceHoleCo cv)
@@ -2217,9 +2324,96 @@ instance Binary IfaceCoercion where
17-> do a <- get bh
b <- get bh
return $ IfaceAxiomRuleCo a b
+ 18-> do r <- get bh
+ t <- get bh
+ dco <- get bh
+ return $ IfaceHydrateDCo r t dco
_ -> panic ("get IfaceCoercion " ++ show tag)
-instance Binary IfaceUnivCoProv where
+instance Binary IfaceDCoercion where
+ put_ bh IfaceReflDCo = do
+ putByte bh 1
+ put_ bh (IfaceGReflLeftDCo a) = do
+ putByte bh 2
+ put_ bh a
+ put_ bh (IfaceGReflRightDCo a) = do
+ putByte bh 3
+ put_ bh a
+ put_ bh (IfaceTyConAppDCo a) = do
+ putByte bh 4
+ put_ bh a
+ put_ bh (IfaceAppDCo a b) = do
+ putByte bh 5
+ put_ bh a
+ put_ bh b
+ put_ bh (IfaceForAllDCo a b c) = do
+ putByte bh 6
+ put_ bh a
+ put_ bh b
+ put_ bh c
+ put_ bh (IfaceCoVarDCo a) = do
+ putByte bh 7
+ put_ bh a
+ put_ bh (IfaceAxiomInstDCo a) = do
+ putByte bh 8
+ put_ bh a
+ put_ bh (IfaceStepsDCo a) = do
+ putByte bh 10
+ put_ bh a
+ put_ bh (IfaceTransDCo a b) = do
+ putByte bh 11
+ put_ bh a
+ put_ bh b
+ put_ bh (IfaceDehydrateCo a) = do
+ putByte bh 12
+ put_ bh a
+ put_ bh (IfaceUnivDCo p rhs) = do
+ putByte bh 13
+ put_ bh p
+ put_ bh rhs
+ put_ bh (IfaceSubDCo dco) = do
+ putByte bh 14
+ put_ bh dco
+ put_ _ (IfaceFreeCoVarDCo cv)
+ = pprPanic "Can't serialise IfaceFreeCoVarDCo" (ppr cv)
+ -- See Note [Holes in IfaceCoercion]
+
+ get bh = do
+ tag <- getByte bh
+ case tag of
+ 1 -> return IfaceReflDCo
+ 2 -> do a <- get bh
+ return $ IfaceGReflLeftDCo a
+ 3 -> do a <- get bh
+ return $ IfaceGReflRightDCo a
+ 4 -> do a <- get bh
+ return $ IfaceTyConAppDCo a
+ 5 -> do a <- get bh
+ b <- get bh
+ return $ IfaceAppDCo a b
+ 6 -> do a <- get bh
+ b <- get bh
+ c <- get bh
+ return $ IfaceForAllDCo a b c
+ 7 -> do a <- get bh
+ return $ IfaceCoVarDCo a
+ 8 -> do a <- get bh
+ return $ IfaceAxiomInstDCo a
+ 10-> do a <- get bh
+ return $ IfaceStepsDCo a
+ 11-> do a <- get bh
+ b <- get bh
+ return $ IfaceTransDCo a b
+ 12 -> do a <- get bh
+ return $ IfaceDehydrateCo a
+ 13 -> do p <- get bh
+ rhs <- get bh
+ return $ IfaceUnivDCo p rhs
+ 14 -> do dco <- get bh
+ return $ IfaceSubDCo dco
+ _ -> panic ("get IfaceDCoercion " ++ show tag)
+
+instance Binary iface_co => Binary (IfaceUnivCoProv iface_co) where
put_ bh (IfacePhantomProv a) = do
putByte bh 1
put_ bh a
@@ -2286,6 +2480,7 @@ instance NFData IfaceCoercion where
IfaceCoVarCo f1 -> rnf f1
IfaceAxiomInstCo f1 f2 f3 -> rnf f1 `seq` rnf f2 `seq` rnf f3
IfaceAxiomRuleCo f1 f2 -> rnf f1 `seq` rnf f2
+ IfaceHydrateDCo f1 f2 f3 -> f1 `seq` rnf f2 `seq` rnf f3
IfaceUnivCo f1 f2 f3 f4 -> rnf f1 `seq` f2 `seq` rnf f3 `seq` rnf f4
IfaceSymCo f1 -> rnf f1
IfaceTransCo f1 f2 -> rnf f1 `seq` rnf f2
@@ -2297,7 +2492,24 @@ instance NFData IfaceCoercion where
IfaceFreeCoVar f1 -> f1 `seq` ()
IfaceHoleCo f1 -> f1 `seq` ()
-instance NFData IfaceUnivCoProv where
+instance NFData IfaceDCoercion where
+ rnf = \case
+ IfaceReflDCo -> ()
+ IfaceGReflRightDCo f1 -> rnf f1
+ IfaceGReflLeftDCo f1 -> rnf f1
+ IfaceTyConAppDCo f1 -> rnf f1
+ IfaceAppDCo f1 f2 -> rnf f1 `seq` rnf f2
+ IfaceForAllDCo f1 f2 f3 -> rnf f1 `seq` rnf f2 `seq` rnf f3
+ IfaceCoVarDCo f1 -> rnf f1
+ IfaceAxiomInstDCo f1 -> rnf f1
+ IfaceStepsDCo f1 -> rnf f1
+ IfaceUnivDCo f1 f2 -> rnf f1 `seq` rnf f2
+ IfaceTransDCo f1 f2 -> rnf f1 `seq` rnf f2
+ IfaceDehydrateCo f1 -> rnf f1
+ IfaceFreeCoVarDCo f1 -> f1 `seq` ()
+ IfaceSubDCo f1 -> f1 `seq` ()
+
+instance NFData (IfaceUnivCoProv co) where
rnf x = seq x ()
instance NFData IfaceMCoercion where
diff --git a/compiler/GHC/Iface/Type.hs-boot b/compiler/GHC/Iface/Type.hs-boot
index 9c10f29ed5..ae26e40f3c 100644
--- a/compiler/GHC/Iface/Type.hs-boot
+++ b/compiler/GHC/Iface/Type.hs-boot
@@ -1,6 +1,7 @@
module GHC.Iface.Type
( IfaceType, IfaceTyCon, IfaceBndr
- , IfaceCoercion, IfaceTyLit, IfaceAppArgs
+ , IfaceCoercion, IfaceDCoercion
+ , IfaceTyLit, IfaceAppArgs
)
where
@@ -14,4 +15,5 @@ data IfaceType
data IfaceTyCon
data IfaceTyLit
data IfaceCoercion
+data IfaceDCoercion
data IfaceBndr
diff --git a/compiler/GHC/IfaceToCore.hs b/compiler/GHC/IfaceToCore.hs
index e37f34ef46..8fafceeb6a 100644
--- a/compiler/GHC/IfaceToCore.hs
+++ b/compiler/GHC/IfaceToCore.hs
@@ -1491,11 +1491,15 @@ tcIfaceCo = go
ForAllCo tv' k' <$> go c }
go (IfaceCoVarCo n) = CoVarCo <$> go_var n
go (IfaceAxiomInstCo n i cs) = AxiomInstCo <$> tcIfaceCoAxiom n <*> pure i <*> mapM go cs
- go (IfaceUnivCo p r t1 t2) = UnivCo <$> tcIfaceUnivCoProv p <*> pure r
+ go (IfaceHydrateDCo r t1 dco)= do { t1 <- tcIfaceType t1
+ ; dco <- tcIfaceDCo dco
+ ; return $ HydrateDCo r t1 dco (followDCo r t1 dco) }
+ -- SLD TODO: investigate perf impact here...
+ -- might be worth storing RHS in the interface file...
+ go (IfaceUnivCo p r t1 t2) = UnivCo <$> tcIfaceUnivCoProv go p <*> pure r
<*> tcIfaceType t1 <*> tcIfaceType t2
go (IfaceSymCo c) = SymCo <$> go c
- go (IfaceTransCo c1 c2) = TransCo <$> go c1
- <*> go c2
+ go (IfaceTransCo co1 co2) = TransCo <$> go co1 <*> go co2
go (IfaceInstCo c1 t2) = InstCo <$> go c1
<*> go t2
go (IfaceSelCo d c) = do { c' <- go c
@@ -1511,11 +1515,34 @@ tcIfaceCo = go
go_var :: FastString -> IfL CoVar
go_var = tcIfaceLclId
-tcIfaceUnivCoProv :: IfaceUnivCoProv -> IfL UnivCoProvenance
-tcIfaceUnivCoProv (IfacePhantomProv kco) = PhantomProv <$> tcIfaceCo kco
-tcIfaceUnivCoProv (IfaceProofIrrelProv kco) = ProofIrrelProv <$> tcIfaceCo kco
-tcIfaceUnivCoProv (IfacePluginProv str) = return $ PluginProv str
-tcIfaceUnivCoProv (IfaceCorePrepProv b) = return $ CorePrepProv b
+tcIfaceDCo :: IfaceDCoercion -> IfL DCoercion
+tcIfaceDCo = go
+ where
+ go IfaceReflDCo = pure ReflDCo
+ go (IfaceGReflRightDCo co) = GReflRightDCo <$> tcIfaceCo co
+ go (IfaceGReflLeftDCo co) = GReflLeftDCo <$> tcIfaceCo co
+ go (IfaceTyConAppDCo cs) = TyConAppDCo <$> mapM go cs
+ go (IfaceAppDCo c1 c2) = AppDCo <$> go c1 <*> go c2
+ go (IfaceForAllDCo tv k c) = do { k' <- tcIfaceDCo k
+ ; bindIfaceBndr tv $ \ tv' ->
+ ForAllDCo tv' k' <$> go c }
+ go (IfaceCoVarDCo n) = CoVarDCo <$> go_var n
+ go (IfaceAxiomInstDCo ax) = AxiomInstDCo <$> tcIfaceCoAxiom ax
+ go (IfaceStepsDCo n) = pure $ StepsDCo n
+ go (IfaceTransDCo co1 co2) = TransDCo <$> go co1 <*> go co2
+ go (IfaceDehydrateCo co) = DehydrateCo <$> tcIfaceCo co
+ go (IfaceUnivDCo prov rhs) = UnivDCo <$> tcIfaceUnivCoProv go prov <*> tcIfaceType rhs
+ go (IfaceSubDCo dco) = SubDCo <$> go dco
+ go (IfaceFreeCoVarDCo c) = pprPanic "tcIfaceDCo:IfaceFreeCoVarDCo" (ppr c)
+
+ go_var :: FastString -> IfL CoVar
+ go_var = tcIfaceLclId
+
+tcIfaceUnivCoProv :: (co -> IfL iface_co) -> IfaceUnivCoProv co -> IfL (UnivCoProvenance iface_co)
+tcIfaceUnivCoProv tc_co (IfacePhantomProv kco) = PhantomProv <$> tc_co kco
+tcIfaceUnivCoProv tc_co (IfaceProofIrrelProv kco) = ProofIrrelProv <$> tc_co kco
+tcIfaceUnivCoProv _ (IfacePluginProv str) = return $ PluginProv str
+tcIfaceUnivCoProv _ (IfaceCorePrepProv b) = return $ CorePrepProv b
{-
************************************************************************
diff --git a/compiler/GHC/Tc/Gen/Foreign.hs b/compiler/GHC/Tc/Gen/Foreign.hs
index 31c42f86d6..b369b43c48 100644
--- a/compiler/GHC/Tc/Gen/Foreign.hs
+++ b/compiler/GHC/Tc/Gen/Foreign.hs
@@ -137,10 +137,10 @@ normaliseFfiType' env ty0 = runWriterT $ go Representational initRecTc ty0
| (bndrs, inner_ty) <- splitForAllForAllTyBinders ty
, not (null bndrs)
= do redn <- go role rec_nts inner_ty
- return $ mkHomoForAllRedn bndrs redn
+ return $ mkHomoForAllRedn bndrs inner_ty redn
| otherwise -- see Note [Don't recur in normaliseFfiType']
- = return $ mkReflRedn role ty
+ = return $ mkReflRedn ty
go_tc_app :: Role -> RecTcChecker -> TyCon -> [Type]
-> WriterT (Bag GlobalRdrElt) TcM Reduction
@@ -168,13 +168,13 @@ normaliseFfiType' env ty0 = runWriterT $ go Representational initRecTc ty0
Just gre ->
do { redn <- go role rec_nts' nt_rhs
; tell (unitBag gre)
- ; return $ nt_co `mkTransRedn` redn } }
+ ; return $ mkDehydrateCoercionRedn nt_co `mkTransRedn` redn } } -- AMG TODO
| isFamilyTyCon tc -- Expand open tycons
- , Reduction co ty <- normaliseTcApp env role tc tys
- , not (isReflexiveCo co)
+ , redn0@(Reduction dco ty) <- normaliseTcApp env role tc tys
+ , not (isReflexiveDCo role (mkTyConApp tc tys) dco ty)
= do redn <- go role rec_nts ty
- return $ co `mkTransRedn` redn
+ return $ redn0 `mkTransRedn` redn
| otherwise
= nothing -- see Note [Don't recur in normaliseFfiType']
@@ -184,12 +184,12 @@ normaliseFfiType' env ty0 = runWriterT $ go Representational initRecTc ty0
= do { args <- unzipRedns <$>
zipWithM ( \ ty r -> go r rec_nts ty )
tys (tyConRoleListX role tc)
- ; return $ mkTyConAppRedn role tc args }
+ ; return $ mkTyConAppRedn tc args }
nt_co = mkUnbranchedAxInstCo role (newTyConCo tc) tys []
nt_rhs = newTyConInstRhs tc tys
ty = mkTyConApp tc tys
- nothing = return $ mkReflRedn role ty
+ nothing = return $ mkReflRedn ty
checkNewtypeFFI :: GlobalRdrEnv -> TyCon -> Maybe GlobalRdrElt
checkNewtypeFFI rdr_env tc
@@ -252,7 +252,7 @@ tcFImport (L dloc fo@(ForeignImport { fd_name = L nloc nm, fd_sig_ty = hs_ty
, fd_fi = imp_decl }))
= setSrcSpanA dloc $ addErrCtxt (foreignDeclCtxt fo) $
do { sig_ty <- tcHsSigType (ForSigCtxt nm) hs_ty
- ; (Reduction norm_co norm_sig_ty, gres) <- normaliseFfiType sig_ty
+ ; (redn@(Reduction _ norm_sig_ty), gres) <- normaliseFfiType sig_ty
; let
-- Drop the foralls before inspecting the
-- structure of the foreign type.
@@ -272,10 +272,12 @@ tcFImport (L dloc fo@(ForeignImport { fd_name = L nloc nm, fd_sig_ty = hs_ty
; imp_decl' <- tcCheckFIType arg_tys res_ty imp_decl
-- Can't use sig_ty here because sig_ty :: Type and
-- we need HsType Id hence the undefined
- ; let fi_decl = ForeignImport { fd_name = L nloc id
- , fd_sig_ty = undefined
- , fd_i_ext = mkSymCo norm_co
- , fd_fi = imp_decl' }
+ ; let co = mkSymCo $ mkHydrateReductionDCoercion Representational sig_ty redn
+ fi_decl =
+ ForeignImport { fd_name = L nloc id
+ , fd_sig_ty = undefined
+ , fd_i_ext = co
+ , fd_fi = imp_decl' }
; return (id, L dloc fi_decl, gres) }
tcFImport d = pprPanic "tcFImport" (ppr d)
@@ -411,7 +413,7 @@ tcFExport fo@(ForeignExport { fd_name = L loc nm, fd_sig_ty = hs_ty, fd_fe = spe
sig_ty <- tcHsSigType (ForSigCtxt nm) hs_ty
rhs <- tcCheckPolyExpr (nlHsVar nm) sig_ty
- (Reduction norm_co norm_sig_ty, gres) <- normaliseFfiType sig_ty
+ (redn@(Reduction _ norm_sig_ty), gres) <- normaliseFfiType sig_ty
spec' <- tcCheckFEType norm_sig_ty spec
@@ -428,7 +430,7 @@ tcFExport fo@(ForeignExport { fd_name = L loc nm, fd_sig_ty = hs_ty, fd_fe = spe
return ( mkVarBind id rhs
, ForeignExport { fd_name = L loc id
, fd_sig_ty = undefined
- , fd_e_ext = norm_co
+ , fd_e_ext = mkHydrateReductionDCoercion Representational sig_ty redn
, fd_fe = spec' }
, gres)
tcFExport d = pprPanic "tcFExport" (ppr d)
diff --git a/compiler/GHC/Tc/Instance/Family.hs b/compiler/GHC/Tc/Instance/Family.hs
index 5e2b523e4b..b5b4a71f50 100644
--- a/compiler/GHC/Tc/Instance/Family.hs
+++ b/compiler/GHC/Tc/Instance/Family.hs
@@ -529,6 +529,7 @@ tcTopNormaliseNewTypeTF_maybe faminsts rdr_env ty
| otherwise
= NS_Done
+-- AMG TODO: refactor tcTopNormaliseNewTypeTF_maybe to just return count of steps taken?
{-
************************************************************************
diff --git a/compiler/GHC/Tc/Solver/Canonical.hs b/compiler/GHC/Tc/Solver/Canonical.hs
index 49210cefa8..5e850c6441 100644
--- a/compiler/GHC/Tc/Solver/Canonical.hs
+++ b/compiler/GHC/Tc/Solver/Canonical.hs
@@ -1010,31 +1010,40 @@ the rewriter set. We check this with an assertion.
-}
-rewriteEvidence rewriters old_ev (Reduction co new_pred) do_next
- | isReflCo co -- See Note [Rewriting with Refl]
+rewriteEvidence rewriters old_ev (Reduction dco new_pred) do_next
+ | isReflDCo dco -- See Note [Rewriting with Refl]
= assert (isEmptyRewriterSet rewriters) $
do_next (setCtEvPredType old_ev new_pred)
rewriteEvidence rewriters ev@(CtGiven { ctev_evar = old_evar, ctev_loc = loc })
- (Reduction co new_pred) do_next
+ (Reduction dco new_pred) do_next
= assert (isEmptyRewriterSet rewriters) $ -- this is a Given, not a wanted
- do { new_ev <- newGivenEvVar loc (new_pred, new_tm)
+ do { let
+ old_pred = ctEvPred ev
+ dco' = downgradeDCoToRepresentational (ctEvRole ev) old_pred dco new_pred
+ co = mkHydrateDCo Representational old_pred dco' new_pred
+
+ -- mkEvCast optimises ReflCo
+ new_tm = mkEvCast (evId old_evar) co
+ ; new_ev <- newGivenEvVar loc (new_pred, new_tm)
; do_next new_ev }
- where
- -- mkEvCast optimises ReflCo
- new_tm = mkEvCast (evId old_evar)
- (downgradeRole Representational (ctEvRole ev) co)
rewriteEvidence new_rewriters
ev@(CtWanted { ctev_dest = dest
, ctev_loc = loc
, ctev_rewriters = rewriters })
- (Reduction co new_pred) do_next
+ (Reduction dco new_pred) do_next
= do { mb_new_ev <- newWanted loc rewriters' new_pred
- ; massert (coercionRole co == ctEvRole ev)
+ ; let
+ old_pred = ctEvPred ev
+ dco' = downgradeDCoToRepresentational (ctEvRole ev) old_pred dco new_pred
+ co = mkHydrateDCo Representational old_pred dco' new_pred
+ -- NB: this call to mkHydrateDCo is OK, because of the invariant
+ -- on the LHS type stored in a Reduction. See Note [The Reduction type]
+ -- in GHC.Core.Reduction.
+
; setWantedEvTerm dest IsCoherent $
- mkEvCast (getEvExpr mb_new_ev)
- (downgradeRole Representational (ctEvRole ev) (mkSymCo co))
+ (mkEvCast (getEvExpr mb_new_ev) (mkSymCo co))
; case mb_new_ev of
Fresh new_ev -> do_next new_ev
Cached _ -> stopWith ev "Cached wanted" }
diff --git a/compiler/GHC/Tc/Solver/Equality.hs b/compiler/GHC/Tc/Solver/Equality.hs
index 6bb894b8b4..95d7b56b1c 100644
--- a/compiler/GHC/Tc/Solver/Equality.hs
+++ b/compiler/GHC/Tc/Solver/Equality.hs
@@ -239,7 +239,8 @@ can_eq_nc' False rdr_env envs ev eq_rel _ ps_ty1 _ ps_ty2
= -- Rewrite the two types and try again
do { (redn1@(Reduction _ xi1), rewriters1) <- rewrite ev ps_ty1
; (redn2@(Reduction _ xi2), rewriters2) <- rewrite ev ps_ty2
- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2
+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped
+ (ps_ty1,redn1) (ps_ty2,redn2)
; traceTcS "can_eq_nc: go round again" (ppr new_ev $$ ppr xi1 $$ ppr xi2)
; can_eq_nc' True rdr_env envs new_ev eq_rel xi1 xi1 xi2 xi2 }
@@ -633,10 +634,12 @@ can_eq_newtype_nc ev swapped ty1 ((gres, co1), ty1') ty2 ps_ty2
-- through newtypes is tantamount to using their constructors.
; recordUsedGREs gres
- ; let redn1 = mkReduction co1 ty1'
+ ; let redn1 = mkReduction (mkDehydrateCo co1) ty1'
+ -- TODO: eliminate dehydration
+
; new_ev <- rewriteEqEvidence emptyRewriterSet ev' swapped
- redn1 (mkReflRedn Representational ps_ty2)
+ (ty1, redn1) (ps_ty2,mkReflRedn ps_ty2)
; can_eq_nc False new_ev ReprEq ty1' ty1' ty2 ps_ty2 }
@@ -712,11 +715,9 @@ canEqCast rewritten ev eq_rel swapped ty1 co1 ty2 ps_ty2
, ppr ty1 <+> text "|>" <+> ppr co1
, ppr ps_ty2 ])
; new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped
- (mkGReflLeftRedn role ty1 co1)
- (mkReflRedn role ps_ty2)
+ (mkCastTy ty1 co1, mkGReflLeftRedn ty1 co1)
+ (ps_ty2, mkReflRedn ps_ty2)
; can_eq_nc rewritten new_ev eq_rel ty1 ty1 ty2 ps_ty2 }
- where
- role = eqRelRole eq_rel
------------------------
canTyConApp :: CtEvidence -> EqRel
@@ -1304,7 +1305,8 @@ canEqFailure ev ReprEq ty1 ty2
-- new equalities become available
; traceTcS "canEqFailure with ReprEq" $
vcat [ ppr ev, ppr redn1, ppr redn2 ]
- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2
+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped
+ (ty1,redn1) (ty2,redn2)
; continueWith (mkIrredCt ReprEqReason new_ev) }
-- | Call when canonicalizing an equality fails with utterly no hope.
@@ -1315,7 +1317,8 @@ canEqHardFailure ev ty1 ty2
= do { traceTcS "canEqHardFailure" (ppr ty1 $$ ppr ty2)
; (redn1, rewriters1) <- rewriteForErrors ev ty1
; (redn2, rewriters2) <- rewriteForErrors ev ty2
- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2
+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped
+ (ty1,redn1) (ty2,redn2)
; continueWith (mkIrredCt ShapeMismatchReason new_ev) }
{-
@@ -1506,8 +1509,8 @@ canEqCanLHSHetero ev eq_rel swapped lhs1 ps_xi1 ki1 xi2 ps_xi2 ki2
-- Otherwise we might put something in the inert set that isn't inert
then startAgainWith (mkNonCanonical ev)
else
- do { let lhs_redn = mkReflRedn role ps_xi1
- rhs_redn = mkGReflRightRedn role xi2 mb_sym_kind_co
+ do { let lhs_redn = mkReflRedn ps_xi1
+ rhs_redn = mkGReflRightRedn xi2 mb_sym_kind_co
mb_sym_kind_co = case swapped of
NotSwapped -> mkSymCo kind_co
IsSwapped -> kind_co
@@ -1515,7 +1518,8 @@ canEqCanLHSHetero ev eq_rel swapped lhs1 ps_xi1 ki1 xi2 ps_xi2 ki2
; traceTcS "Hetero equality gives rise to kind equality"
(ppr swapped $$
ppr kind_co <+> dcolon <+> sep [ ppr ki1, text "~#", ppr ki2 ])
- ; type_ev <- rewriteEqEvidence rewriters ev swapped lhs_redn rhs_redn
+ ; type_ev <- rewriteEqEvidence rewriters ev swapped
+ (xi1,lhs_redn) (xi2,rhs_redn)
; let new_xi2 = mkCastTy ps_xi2 mb_sym_kind_co
; canEqCanLHSHomo type_ev eq_rel NotSwapped lhs1 ps_xi1 new_xi2 new_xi2 }}
@@ -1540,7 +1544,6 @@ canEqCanLHSHetero ev eq_rel swapped lhs1 ps_xi1 ki1 xi2 ps_xi2 ki2
; return (kind_co, rewriterSetFromCts cts, not (null unifs)) }
xi1 = canEqLHSType lhs1
- role = eqRelRole eq_rel
canEqCanLHSHomo :: CtEvidence -- lhs ~ rhs
-- or, if swapped: rhs ~ lhs
@@ -1626,7 +1629,6 @@ canEqCanLHS2 ev eq_rel swapped lhs1 ps_xi1 lhs2 ps_xi2 mco
else finish_without_swapping } }
where
sym_mco = mkSymMCo mco
- role = eqRelRole eq_rel
lhs1_ty = canEqLHSType lhs1
lhs2_ty = canEqLHSType lhs2
@@ -1639,9 +1641,10 @@ canEqCanLHS2 ev eq_rel swapped lhs1 ps_xi1 lhs2 ps_xi2 mco
-- where grefl1 : lhs1 ~ lhs1 |> sym co
-- grefl2 : lhs2 ~ lhs2 |> co
finish_with_swapping
- = do { let lhs1_redn = mkGReflRightMRedn role lhs1_ty sym_mco
- lhs2_redn = mkGReflLeftMRedn role lhs2_ty mco
- ; new_ev <-rewriteEqEvidence emptyRewriterSet ev swapped lhs1_redn lhs2_redn
+ = do { let lhs1_redn = mkGReflRightMRedn lhs1_ty sym_mco
+ lhs2_redn = mkGReflLeftMRedn lhs2_ty mco
+ ; new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped
+ (lhs1_ty, lhs1_redn) (mkCastTyMCo lhs2_ty mco, lhs2_redn)
; canEqCanLHSFinish new_ev eq_rel IsSwapped lhs2 (ps_xi1 `mkCastTyMCo` sym_mco) }
put_tyvar_on_lhs = isWanted ev && eq_rel == NomEq
@@ -1772,7 +1775,7 @@ canEqCanLHSFinish_try_unification ev eq_rel swapped lhs rhs
-> canEqCanLHSFinish_no_unification ev eq_rel swapped lhs rhs
| otherwise
- -> tryIrredInstead reason ev eq_rel swapped lhs rhs ;
+ -> tryIrredInstead reason ev swapped lhs rhs ;
PuOK _ rhs_redn ->
@@ -1783,10 +1786,11 @@ canEqCanLHSFinish_try_unification ev eq_rel swapped lhs rhs
-- We unify alpha := Int, and set co := <Int>. No need to
-- swap to co = sym co'
-- co' = <Int>
- new_ev <- if isReflCo (reductionCoercion rhs_redn)
+ new_ev <- if isReflDCo (reductionDCoercion rhs_redn)
then return ev
- else rewriteEqEvidence emptyRewriterSet ev swapped
- (mkReflRedn Nominal (mkTyVarTy tv)) rhs_redn
+ else let lhs = mkTyVarTy tv
+ in rewriteEqEvidence emptyRewriterSet ev swapped
+ (lhs, mkReflRedn lhs) (rhs, rhs_redn)
; let tv_ty = mkTyVarTy tv
final_rhs = reductionReducedType rhs_redn
@@ -1848,12 +1852,12 @@ canEqCanLHSFinish_no_unification ev eq_rel swapped lhs rhs
-- -> swapAndFinish ev eq_rel swapped lhs_ty can_rhs
-- | otherwise
- -> tryIrredInstead reason ev eq_rel swapped lhs rhs
+ -> tryIrredInstead reason ev swapped lhs rhs
PuOK _ rhs_redn
-> do { new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped
- (mkReflRedn (eqRelRole eq_rel) lhs_ty)
- rhs_redn
+ (lhs_ty, mkReflRedn lhs_ty)
+ (rhs, rhs_redn)
-- Important: even if the coercion is Refl,
-- * new_ev has reductionReducedType on the RHS
@@ -1871,30 +1875,28 @@ swapAndFinish :: CtEvidence -> EqRel -> SwapFlag
-- mentions alpha, it would not be a canonical constraint as-is.
-- We want to flip it to (F tys ~ a), whereupon it is canonical
swapAndFinish ev eq_rel swapped lhs_ty can_rhs
- = do { new_ev <- rewriteEqEvidence emptyRewriterSet ev (flipSwap swapped)
- (mkReflRedn role (canEqLHSType can_rhs))
- (mkReflRedn role lhs_ty)
+ = do { let rhs = canEqLHSType can_rhs
+ ; new_ev <- rewriteEqEvidence emptyRewriterSet ev (flipSwap swapped)
+ (rhs, mkReflRedn rhs)
+ (lhs_ty, mkReflRedn lhs_ty)
; interactEq (EqCt { eq_ev = new_ev, eq_eq_rel = eq_rel
, eq_lhs = can_rhs, eq_rhs = lhs_ty }) }
- where
- role = eqRelRole eq_rel
----------------------
-tryIrredInstead :: CheckTyEqResult -> CtEvidence -> EqRel -> SwapFlag
+tryIrredInstead :: CheckTyEqResult -> CtEvidence -> SwapFlag
-> CanEqLHS -> TcType -> TcS (StopOrContinue Ct)
-- We have a non-canonical equality
-- We still swap it if 'swapped' says so, so that it is oriented
-- in the direction that the error message reporting machinery
-- expects it; e.g. (m ~ t m) rather than (t m ~ m)
-- This is not very important, and only affects error reporting.
-tryIrredInstead reason ev eq_rel swapped lhs rhs
+tryIrredInstead reason ev swapped lhs rhs
= do { traceTcS "cantMakeCanonical" (ppr reason $$ ppr lhs $$ ppr rhs)
+ ; let lhs_ty = canEqLHSType lhs
; new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped
- (mkReflRedn role (canEqLHSType lhs))
- (mkReflRedn role rhs)
+ (lhs_ty, mkReflRedn lhs_ty)
+ (rhs, mkReflRedn rhs)
; solveIrredEquality (NonCanonicalReason reason) new_ev }
- where
- role = eqRelRole eq_rel
-----------------------
-- | Solve a reflexive equality constraint
@@ -2386,8 +2388,8 @@ rewriteEqEvidence :: RewriterSet -- New rewriters
-> CtEvidence -- Old evidence :: olhs ~ orhs (not swapped)
-- or orhs ~ olhs (swapped)
-> SwapFlag
- -> Reduction -- lhs_co :: olhs ~ nlhs
- -> Reduction -- rhs_co :: orhs ~ nrhs
+ -> (Type, Reduction) -- lhs_co :: olhs ~ nlhs
+ -> (Type, Reduction) -- rhs_co :: orhs ~ nrhs
-> TcS CtEvidence -- Of type nlhs ~ nrhs
-- With reductions (Reduction lhs_co nlhs) (Reduction rhs_co nrhs),
-- rewriteEqEvidence yields, for a given equality (Given g olhs orhs):
@@ -2404,10 +2406,11 @@ rewriteEqEvidence :: RewriterSet -- New rewriters
-- w : orhs ~ olhs = rhs_co ; sym w1 ; sym lhs_co
--
-- It's all a form of rewriteEvidence, specialised for equalities
-rewriteEqEvidence new_rewriters old_ev swapped (Reduction lhs_co nlhs) (Reduction rhs_co nrhs)
+rewriteEqEvidence new_rewriters old_ev swapped (olhs, lhs_redn@(Reduction lhs_dco nlhs))
+ (orhs, rhs_redn@(Reduction rhs_dco nrhs))
| NotSwapped <- swapped
- , isReflCo lhs_co -- See Note [Rewriting with Refl]
- , isReflCo rhs_co
+ , isReflDCo lhs_dco -- See Note [Rewriting with Refl]
+ , isReflDCo rhs_dco
= return (setCtEvPredType old_ev new_pred)
| CtGiven { ctev_evar = old_evar } <- old_ev
@@ -2437,6 +2440,8 @@ rewriteEqEvidence new_rewriters old_ev swapped (Reduction lhs_co nlhs) (Reductio
where
new_pred = mkTcEqPredLikeEv old_ev nlhs nrhs
loc = ctEvLoc old_ev
+ lhs_co = mkHydrateReductionDCoercion (ctEvRole old_ev) olhs lhs_redn
+ rhs_co = mkHydrateReductionDCoercion (ctEvRole old_ev) orhs rhs_redn
{-
**********************************************************************
@@ -2678,7 +2683,6 @@ final_qci_check work_ct eq_rel lhs rhs
where
ev = ctEvidence work_ct
loc = ctEvLoc ev
- role = eqRelRole eq_rel
try_for_qci -- First try looking for (lhs ~ rhs)
| Just (cls, tys) <- boxEqPred eq_rel lhs rhs
@@ -2698,7 +2702,7 @@ final_qci_check work_ct eq_rel lhs rhs
; case res of
OneInst { cir_mk_ev = mk_ev }
-> do { ev' <- rewriteEqEvidence emptyRewriterSet ev IsSwapped
- (mkReflRedn role rhs) (mkReflRedn role lhs)
+ (rhs, mkReflRedn rhs) (lhs, mkReflRedn lhs)
; chooseInstance ev' (res { cir_mk_ev = mk_eq_ev cls tys mk_ev }) }
_ -> do { traceTcS "final_qci_check:3" (ppr work_ct)
; continueWith work_ct }}
diff --git a/compiler/GHC/Tc/Solver/Monad.hs b/compiler/GHC/Tc/Solver/Monad.hs
index 91e20becf8..35d560f9a7 100644
--- a/compiler/GHC/Tc/Solver/Monad.hs
+++ b/compiler/GHC/Tc/Solver/Monad.hs
@@ -762,7 +762,8 @@ lookupFamAppInert rewrite_pred fam_tc tys
| Just ecl <- findFunEq inert_funeqs fam_tc tys
, Just (EqCt { eq_ev = ctev, eq_rhs = rhs })
<- find (rewrite_pred . eqCtFlavourRole) ecl
- = Just (mkReduction (ctEvCoercion ctev) rhs, ctEvFlavourRole ctev)
+ = Just (mkReduction (mkDehydrateCo (ctEvCoercion ctev)) rhs -- SLD TODO: avoid dehydrating?
+ ,ctEvFlavourRole ctev)
| otherwise = Nothing
lookupInInerts :: CtLoc -> TcPredType -> TcS (Maybe CtEvidence)
@@ -812,7 +813,6 @@ lookupFamAppCache fam_tc tys
Nothing -> return Nothing }
extendFamAppCache :: TyCon -> [Type] -> Reduction -> TcS ()
--- NB: co :: rhs ~ F tys, to match expectations of rewriter
extendFamAppCache tc xi_args stuff@(Reduction _ ty)
= do { dflags <- getDynFlags
; when (gopt Opt_FamAppCache dflags) $
@@ -831,7 +831,7 @@ dropFromFamAppCache varset
where
check :: Reduction -> Bool
check redn
- = not (anyFreeVarsOfCo (`elemVarSet` varset) $ reductionCoercion redn)
+ = not (anyFreeVarsOfDCo (`elemVarSet` varset) $ reductionDCoercion redn)
{- *********************************************************************
* *
@@ -892,16 +892,17 @@ data TcSEnv
---------------
newtype TcS a = TcS { unTcS :: TcSEnv -> TcM a }
- deriving (Functor)
-
-instance MonadFix TcS where
- mfix k = TcS $ \env -> mfix (\x -> unTcS (k x) env)
-- | Smart constructor for 'TcS', as describe in Note [The one-shot state
-- monad trick] in "GHC.Utils.Monad".
mkTcS :: (TcSEnv -> TcM a) -> TcS a
mkTcS f = TcS (oneShot f)
+-- Use the one-shot trick for the functor instance of 'TcS'.
+instance Functor TcS where
+ fmap f m = mkTcS $ \env ->
+ fmap f $ unTcS m env
+
instance Applicative TcS where
pure x = mkTcS $ \_ -> return x
(<*>) = ap
@@ -913,6 +914,9 @@ instance Monad TcS where
instance MonadIO TcS where
liftIO act = TcS $ \_env -> liftIO act
+instance MonadFix TcS where
+ mfix k = TcS $ \env -> mfix (\x -> unTcS (k x) env)
+
instance MonadFail TcS where
fail err = mkTcS $ \_ -> fail err
@@ -2103,7 +2107,7 @@ checkTouchableTyVarEq ev lhs_tv rhs
| simpleUnifyCheck True lhs_tv rhs
-- True <=> type families are ok on the RHS
= do { traceTcS "checkTouchableTyVarEq: simple-check wins" (ppr lhs_tv $$ ppr rhs)
- ; return (pure (mkReflRedn Nominal rhs)) }
+ ; return (pure (mkReflRedn rhs)) }
| otherwise
= do { traceTcS "checkTouchableTyVarEq {" (ppr lhs_tv $$ ppr rhs)
@@ -2165,8 +2169,8 @@ checkTouchableTyVarEq ev lhs_tv rhs
, ctev_dest = HoleDest hole
, ctev_loc = cb_loc
, ctev_rewriters = ctEvRewriters ev }
- ; return (PuOK (singleCt (mkNonCanonical new_ev))
- (mkReduction (HoleCo hole) new_tv_ty)) } }
+ redn = mkDehydrateCoercionRedn (HoleCo hole)
+ ; return (PuOK (singleCt (mkNonCanonical new_ev)) redn) } }
-- See Detail (7) of the Note
cb_loc = updateCtLocOrigin (ctEvLoc ev) CycleBreakerOrigin
@@ -2231,7 +2235,7 @@ checkTypeEq ev eq_rel lhs rhs
break_given fam_app
= do { new_tv <- TcM.newCycleBreakerTyVar (typeKind fam_app)
; return (PuOK (unitBag (new_tv, fam_app))
- (mkReflRedn Nominal (mkTyVarTy new_tv))) }
+ (mkReflRedn (mkTyVarTy new_tv))) }
-- Why reflexive? See Detail (4) of the Note
---------------------------
diff --git a/compiler/GHC/Tc/Solver/Rewrite.hs b/compiler/GHC/Tc/Solver/Rewrite.hs
index 64d590cbe9..6713019e35 100644
--- a/compiler/GHC/Tc/Solver/Rewrite.hs
+++ b/compiler/GHC/Tc/Solver/Rewrite.hs
@@ -1,5 +1,4 @@
{-# LANGUAGE BangPatterns #-}
-
{-# LANGUAGE DeriveFunctor #-}
module GHC.Tc.Solver.Rewrite(
@@ -56,7 +55,10 @@ import qualified GHC.Data.List.Infinite as Inf
-- | The 'RewriteM' monad is a wrapper around 'TcS' with a 'RewriteEnv'
newtype RewriteM a
= RewriteM { runRewriteM :: RewriteEnv -> TcS a }
- deriving (Functor)
+
+-- Use the one-shot trick for the functor instance of 'RewriteM'.
+instance Functor RewriteM where
+ fmap f m = mkRewriteM $ \env -> fmap f $ runRewriteM m env
-- | Smart constructor for 'RewriteM', as describe in Note [The one-shot state
-- monad trick] in "GHC.Utils.Monad".
@@ -92,11 +94,11 @@ runRewriteCtEv ev
runRewrite :: CtLoc -> CtFlavour -> EqRel -> RewriteM a -> TcS (a, RewriterSet)
runRewrite loc flav eq_rel thing_inside
= do { rewriters_ref <- newTcRef emptyRewriterSet
- ; let fmode = RE { re_loc = loc
+ ; let rmode = RE { re_loc = loc
, re_flavour = flav
, re_eq_rel = eq_rel
, re_rewriters = rewriters_ref }
- ; res <- runRewriteM thing_inside fmode
+ ; res <- runRewriteM thing_inside rmode
; rewriters <- readTcRef rewriters_ref
; return (res, rewriters) }
@@ -212,16 +214,19 @@ a better error message anyway.)
-}
-- | See Note [Rewriting].
--- If (xi, co, rewriters) <- rewrite mode ev ty, then co :: xi ~r ty
+-- If (Reduction ty' dco xi, rewriters) <- rewrite mode ev ty, then dco :: ty' ~r xi
-- where r is the role in @ev@.
--- rewriters is the set of coercion holes that have been used to rewrite
+-- @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, RewriterSet)
rewrite ev ty
= do { traceTcS "rewrite {" (ppr ty)
; result@(redn, _) <- runRewriteCtEv ev (rewrite_one ty)
- ; traceTcS "rewrite }" (ppr $ reductionReducedType redn)
+ ; traceTcS "rewrite }" $
+ vcat [ text "ty:" <+> ppr ty
+ , text "dco:" <+> ppr (reductionDCoercion redn)
+ , text "xi:" <+> ppr (reductionReducedType redn) ]
; return result }
-- | See Note [Rewriting]
@@ -238,7 +243,7 @@ rewriteForErrors ev ty
; traceTcS "rewriteForErrors }" (ppr $ reductionReducedType redn)
; return $ case ctEvEqRel ev of
NomEq -> result
- ReprEq -> (mkSubRedn redn, rewriters) }
+ ReprEq -> (mkSubRedn ty redn, rewriters) }
-- See Note [Rewriting]
rewriteArgsNom :: CtEvidence -> TyCon -> [TcType]
@@ -254,11 +259,11 @@ rewriteArgsNom :: CtEvidence -> TyCon -> [TcType]
-- 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, rewriters)
+ = do { traceTcS "rewriteArgsNom {" (vcat (map ppr tys))
+ ; (ArgsReductions redns@(Reductions _ tys') kind_dco, rewriters)
<- runRewriteCtEv ev (rewrite_args_tc tc Nothing tys)
- ; massert (isReflMCo kind_co)
- ; traceTcS "rewrite }" (vcat (map ppr tys'))
+ ; massert (isReflMCo kind_dco)
+ ; traceTcS "rewriteArgsNom }" (vcat (map ppr tys'))
; return (redns, rewriters) }
-- | Rewrite a type w.r.t. nominal equality. This is useful to rewrite
@@ -282,16 +287,16 @@ rewriteType loc ty
********************************************************************* -}
{- Note [Rewriting]
-~~~~~~~~~~~~~~~~~~~~
- rewrite ty ==> Reduction co xi
+~~~~~~~~~~~~~~~~~~~
+ rewrite ty (at role r) ==> Reduction ty' dco xi
where
xi has no reducible type functions
has no skolems that are mapped in the inert set
has no filled-in metavariables
- co :: ty ~ xi (coercions in reductions are always left-to-right)
+ dco :: ty' ~r xi (coercions in reductions are always left-to-right)
Key invariants:
- (F0) co :: zonk(ty') ~ xi where zonk(ty') ~ zonk(ty)
+ (F0) dco :: zonk(ty') ~ xi where zonk(ty') ~ zonk(ty)
(F1) typeKind(xi) succeeds and returns a fully zonked kind
(F2) typeKind(xi) `eqType` zonk(typeKind(ty))
@@ -301,18 +306,11 @@ 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 :: ty ~ xi) isn't quite true. So, instead,
+Because rewriting zonks and the returned directed coercion ("dco" above)
+is also zonked, it's possible that (dco :: ty ~r xi) isn't quite true. So, instead,
we can rely on this fact:
- (F0) co :: zonk(ty') ~ xi, where zonk(ty') ~ zonk(ty)
-
-Note that the right-hand type of co is *always* precisely xi. The left-hand
-type may or may not be ty, however: if ty has unzonked filled-in metavariables,
-then the left-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 :: ty ~ xi) is important even before we zonk the whole program.
-For example, see the RTRNotFollowed case in rewriteTyVar.
+ (F0) dco :: ty' ~r xi, where zonk(ty') ~ zonk(ty)
Why have these invariants on rewriting? Because we sometimes use typeKind
during canonicalisation, and we want this kind to be zonked (e.g., see
@@ -389,7 +387,7 @@ rewrite_args_tc
-- Otherwise: no assumptions; use roles provided
-> [Type]
-> RewriteM ArgsReductions -- See the commentary on rewrite_args
-rewrite_args_tc tc = rewrite_args all_bndrs any_named_bndrs inner_ki emptyVarSet
+rewrite_args_tc tc roles args = rewrite_args all_bndrs any_named_bndrs inner_ki emptyVarSet roles args
-- NB: TyCon kinds are always closed
where
-- There are many bang patterns in here. It's been observed that they
@@ -459,7 +457,12 @@ rewrite_args_slow :: [PiTyBinder] -> Kind -> TcTyCoVarSet
-> RewriteM ArgsReductions
rewrite_args_slow binders inner_ki fvs roles tys
= do { rewritten_args <- zipWithM rw (Inf.toList roles) tys
- ; return (simplifyArgsWorker binders inner_ki fvs roles rewritten_args) }
+ -- NB: this is the crucial place where we require the hydration invariant
+ -- to be satisfied. This is achieved by having Reduction store a LHS type.
+ -- See Note [The Reduction type] in GHC.Core.Reduction,
+ -- and Note [Following a directed coercion] in GHC.Core.Coercion.
+ -- Relevant test case: T13333.
+ ; return $ simplifyArgsWorker binders inner_ki fvs roles tys rewritten_args }
where
{-# INLINE rw #-}
rw :: Role -> Type -> RewriteM Reduction
@@ -474,7 +477,7 @@ rewrite_args_slow binders inner_ki fvs roles tys
rw Phantom ty
-- See Note [Phantoms in the rewriter]
= do { ty <- liftTcS $ zonkTcType ty
- ; return $ mkReflRedn Phantom ty }
+ ; return $ mkReflRedn ty }
------------------
rewrite_one :: TcType -> RewriteM Reduction
@@ -490,8 +493,7 @@ rewrite_one ty
= rewrite_one ty'
rewrite_one xi@(LitTy {})
- = do { role <- getRole
- ; return $ mkReflRedn role xi }
+ = return $ mkReflRedn xi
rewrite_one (TyVarTy tv)
= rewriteTyVar tv
@@ -518,28 +520,34 @@ rewrite_one (FunTy { ft_af = vis, ft_mult = mult, ft_arg = ty1, ft_res = ty2 })
; let arg_rep = getRuntimeRep (reductionReducedType arg_redn)
res_rep = getRuntimeRep (reductionReducedType res_redn)
- ; (w_redn, arg_rep_redn, res_rep_redn) <- setEqRel NomEq $
- liftA3 (,,) (rewrite_one mult)
- (rewrite_one arg_rep)
- (rewrite_one res_rep)
- ; role <- getRole
+ ; ( w_redn
+ , Reduction arg_rep_dco arg_rep_xi
+ , Reduction res_rep_dco res_rep_xi
+ ) <- setEqRel NomEq $
+ liftA3 (,,) (rewrite_one mult)
+ (rewrite_one arg_rep)
+ (rewrite_one res_rep)
- ; let arg_rep_co = reductionCoercion arg_rep_redn
+ ; let arg_rep_co = mkHydrateDCo Nominal arg_rep arg_rep_dco arg_rep_xi
-- :: arg_rep ~ arg_rep_xi
arg_ki_co = mkTyConAppCo Nominal tYPETyCon [arg_rep_co]
-- :: TYPE arg_rep ~ TYPE arg_rep_xi
- casted_arg_redn = mkCoherenceRightRedn role arg_redn arg_ki_co
+ casted_arg_redn = mkCoherenceRightRedn arg_redn arg_ki_co
-- :: ty1 ~> arg_xi |> arg_ki_co
- res_rep_co = reductionCoercion res_rep_redn
+ res_rep_co = mkHydrateDCo Nominal res_rep res_rep_dco res_rep_xi
res_ki_co = mkTyConAppCo Nominal tYPETyCon [res_rep_co]
- casted_res_redn = mkCoherenceRightRedn role res_redn res_ki_co
+ casted_res_redn = mkCoherenceRightRedn res_redn res_ki_co
+
+ -- NB: these two calls to mkHydrateDCo are OK, because of the invariant
+ -- on the LHS type stored in a Reduction. See Note [The Reduction type]
+ -- in GHC.Core.Reduction.
-- We must rewrite the representations, because that's what would
-- be done if we used TyConApp instead of FunTy. These rewritten
-- representations are seen only in casts of the arg and res, below.
-- Forgetting this caused #19677.
- ; return $ mkFunRedn role vis w_redn casted_arg_redn casted_res_redn }
+ ; return $ mkFunRedn vis w_redn arg_rep_dco res_rep_dco casted_arg_redn casted_res_redn }
rewrite_one ty@(ForAllTy {})
-- TODO (RAE): This is inadequate, as it doesn't rewrite the kind of
@@ -550,13 +558,12 @@ rewrite_one ty@(ForAllTy {})
-- applications inside the forall involve the bound type variables.
= do { let (bndrs, rho) = tcSplitForAllTyVarBinders ty
; redn <- rewrite_one rho
- ; return $ mkHomoForAllRedn bndrs redn }
+ ; return $ mkHomoForAllRedn bndrs rho redn }
rewrite_one (CastTy ty g)
= do { redn <- rewrite_one ty
; g' <- rewrite_co g
- ; role <- getRole
- ; return $ mkCastRedn1 role ty g' redn }
+ ; return $ mkCastRedn1 g' redn }
-- This calls castCoercionKind1.
-- It makes a /big/ difference to call castCoercionKind1 not
-- the more general castCoercionKind2.
@@ -564,8 +571,7 @@ rewrite_one (CastTy ty g)
rewrite_one (CoercionTy co)
= do { co' <- rewrite_co co
- ; role <- getRole
- ; return $ mkReflCoRedn role co' }
+ ; return $ mkReflCoRedn co' }
-- | "Rewrite" a coercion. Really, just zonk it so we can uphold
-- (F1) of Note [Rewriting]
@@ -574,9 +580,9 @@ rewrite_co co = liftTcS $ zonkCo co
-- | Rewrite a reduction, composing the resulting coercions.
rewrite_reduction :: Reduction -> RewriteM Reduction
-rewrite_reduction (Reduction co xi)
+rewrite_reduction redn0@(Reduction _ xi)
= do { redn <- bumpDepth $ rewrite_one xi
- ; return $ co `mkTransRedn` redn }
+ ; return $ redn0 `mkTransRedn` redn }
-- rewrite (nested) AppTys
rewrite_app_tys :: Type -> [Type] -> RewriteM Reduction
@@ -598,44 +604,38 @@ rewrite_app_ty_args :: Reduction -> [Type] -> RewriteM Reduction
rewrite_app_ty_args redn []
-- this will be a common case when called from rewrite_fam_app, so shortcut
= return redn
-rewrite_app_ty_args fun_redn@(Reduction fun_co fun_xi) arg_tys
- = do { het_redn <- case tcSplitTyConApp_maybe fun_xi of
- Just (tc, xis) ->
- do { let tc_roles = tyConRolesRepresentational tc
- arg_roles = Inf.dropList xis tc_roles
- ; ArgsReductions (Reductions arg_cos arg_xis) kind_co
- <- rewrite_vector (typeKind fun_xi) arg_roles arg_tys
-
- -- We start with a reduction of the form
- -- fun_co :: ty ~ T xi_1 ... xi_n
- -- and further arguments a_1, ..., a_m.
- -- We rewrite these arguments, and obtain coercions:
- -- arg_co_i :: a_i ~ zeta_i
- -- Now, 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:
- -- fun_co <a_1> ... <a_m> ;; T <xi_1> .. <xi_n> arg_co_1 ... arg_co_m
- ; eq_rel <- getEqRel
- ; let app_xi = mkTyConApp tc (xis ++ arg_xis)
- app_co = case eq_rel of
- NomEq -> mkAppCos fun_co arg_cos
- ReprEq -> mkAppCos fun_co (map mkNomReflCo arg_tys)
- `mkTransCo`
- mkTyConAppCo Representational tc
- (zipWith mkReflCo (Inf.toList tc_roles) xis ++ arg_cos)
-
- ; return $
- mkHetReduction
- (mkReduction app_co app_xi )
- kind_co }
- Nothing ->
- do { ArgsReductions redns kind_co
- <- rewrite_vector (typeKind fun_xi) (Inf.repeat Nominal) arg_tys
- ; return $ mkHetReduction (mkAppRedns fun_redn redns) kind_co }
-
- ; role <- getRole
- ; return (homogeniseHetRedn role het_redn) }
+rewrite_app_ty_args fun_redn@(Reduction fun_co fun_xi) more_arg_tys
+ = case tcSplitTyConApp_maybe fun_xi of
+ Just (tc, xis) ->
+ do { let tc_roles = tyConRolesRepresentational tc
+ arg_roles = Inf.dropList xis tc_roles
+ ; ArgsReductions (Reductions arg_cos arg_xis) kind_co
+ <- rewrite_vector (typeKind fun_xi) arg_roles more_arg_tys
+
+ -- We start with a reduction of the form
+ -- fun_co :: ty ~ T xi_1 ... xi_n
+ -- and further arguments a_1, ..., a_m.
+ -- We rewrite these arguments, and obtain coercions:
+ -- arg_co_i :: a_i ~ zeta_i
+ -- Now, 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:
+ -- fun_co <a_1> ... <a_m> ;; T <xi_1> .. <xi_n> arg_co_1 ... arg_co_m
+
+ ; eq_rel <- getEqRel
+ ; let app_xi = mkTyConApp tc (xis ++ arg_xis)
+ app_co = case eq_rel of
+ NomEq -> mkAppDCos fun_co arg_cos
+ ReprEq -> mkAppDCos fun_co (mkReflDCos more_arg_tys)
+ `mkTransDCo`
+ mkTyConAppDCo (mkReflDCos xis ++ arg_cos)
+
+ ; return $ homogeniseRedn (mkReduction app_co app_xi) kind_co }
+ Nothing ->
+ do { ArgsReductions redns kind_co
+ <- rewrite_vector (typeKind fun_xi) (Inf.repeat Nominal) more_arg_tys
+ ; return $ homogeniseRedn (mkAppRedns fun_redn redns) kind_co }
rewrite_ty_con_app :: TyCon -> [TcType] -> RewriteM Reduction
rewrite_ty_con_app tc tys
@@ -643,11 +643,10 @@ rewrite_ty_con_app tc tys
; let m_roles | Nominal <- role = Nothing
| otherwise = Just $ tyConRolesX role tc
; ArgsReductions redns kind_co <- rewrite_args_tc tc m_roles tys
- ; let tyconapp_redn
- = mkHetReduction
- (mkTyConAppRedn role tc redns)
- kind_co
- ; return $ homogeniseHetRedn role tyconapp_redn }
+ ; return $ homogeniseRedn
+ (mkTyConAppRedn_MightBeSynonym role tc tys redns)
+ kind_co }
+{-# INLINE rewrite_ty_con_app #-}
-- Rewrite a vector (list of arguments).
rewrite_vector :: Kind -- of the function being applied to these arguments
@@ -758,8 +757,8 @@ STEP 5: GIVEUP. No progress to be made. Return what we have. (Do not 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.
+FINISH 2. Add the result to the famapp-cache, to speed things up next time we
+ come across the same type family application.
Because STEP 1{a,b,c} and STEP 4{a,b,c} happen the same way, they are abstracted into
try_to_reduce.
@@ -773,7 +772,6 @@ is inlined in that case, and only FINISH 1 is performed.
rewrite_fam_app :: TyCon -> [TcType] -> RewriteM Reduction
-- 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
= assertPpr (tys `lengthAtLeast` tyConArity tc)
(ppr tc $$ ppr (tyConArity tc) $$ ppr tys) $
@@ -782,15 +780,17 @@ rewrite_fam_app tc tys -- Can be over-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
- ; redn <- rewrite_exact_fam_app tc tys1
+ do { let (!tys1, !tys_rest)
+ | length tys > tyConArity tc = splitAt (tyConArity tc) tys
+ | otherwise = (tys, [])
+ ; !redn <- rewrite_exact_fam_app tc tys1
; rewrite_app_ty_args redn tys_rest }
-- the [TcType] exactly saturate the TyCon
-- See Note [How to normalise a family application]
rewrite_exact_fam_app :: TyCon -> [TcType] -> RewriteM Reduction
rewrite_exact_fam_app tc tys
- = do { checkStackDepth (mkTyConApp tc tys)
+ = do { checkStackDepth $ mkTyConApp tc tys
-- Query the typechecking plugins for all their rewriting functions
-- which apply to a type family application headed by the TyCon 'tc'.
@@ -801,17 +801,12 @@ rewrite_exact_fam_app tc tys
; case result1 of
-- Don't use the cache;
-- See Note [rewrite_exact_fam_app performance]
- { Just redn -> finish False redn
+ { Just redn -> finish Don'tAddToCache redn
; Nothing ->
-- That didn't work. So reduce the arguments, in STEP 2.
- do { eq_rel <- getEqRel
- -- checking eq_rel == NomEq saves ~0.5% in T9872a
- ; ArgsReductions (Reductions cos xis) kind_co <-
- if eq_rel == NomEq
- then rewrite_args_tc tc Nothing tys
- else setEqRel NomEq $
- rewrite_args_tc tc Nothing tys
+ do { (ArgsReductions redns@(Reductions _ xis) kind_co) <-
+ setEqRel NomEq $ rewrite_args_tc tc Nothing tys
-- If we manage to rewrite the type family application after
-- rewriting the arguments, we will need to compose these
@@ -826,63 +821,86 @@ rewrite_exact_fam_app tc tys
--
-- full_co :: F ty_1 ... ty_n ~ zeta
-- full_co = F co_1 ... co_n ;; fam_co
- ; let
- role = eqRelRole eq_rel
- args_co = mkTyConAppCo role tc cos
- ; let homogenise :: Reduction -> Reduction
- homogenise redn
- = homogeniseHetRedn role
- $ mkHetReduction
- (args_co `mkTransRedn` redn)
- kind_co
-
- give_up :: Reduction
- give_up = homogenise $ mkReflRedn role reduced
- where reduced = mkTyConApp tc xis
+ ; let args_redn :: Reduction
+ !args_redn = mkTyConAppRedn tc redns
+ homogenise :: Reduction -> Reduction
+ homogenise redn
+ = homogeniseRedn
+ (args_redn `mkTransRedn` redn)
+ kind_co
+
+ give_up :: Reduction
+ give_up = homogenise $ mkReflRedn reduced
+ where reduced = mkTyConApp tc xis
-- STEP 3: try the inerts
- ; flavour <- getFlavour
- ; result2 <- liftTcS $ lookupFamAppInert (`eqCanRewriteFR` (flavour, eq_rel)) tc xis
+ ; flavour_role@(_, eq_rel) <- getFlavourRole
+ ; result2 <- liftTcS $ lookupFamAppInert (`eqCanRewriteFR` flavour_role) tc xis
; case result2 of
- { 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
+ { Just (redn, (inert_flavour, inert_eq_rel)) ->
+ do { traceRewriteM "rewrite family application with inert" $
+ ( ppr tc <+> ppr xis $$ ppr redn)
+ ; let use_cache :: AddToCache
+ !use_cache
+ -- Don't add something to the cache if the reduction
+ -- contains a coercion hole.
+ | inert_flavour == Given
+ = RewroteArgsAddToCache
+ | otherwise
+ = Don'tAddToCache
+ ; finish use_cache (homogenise downgraded_redn) }
where
inert_role = eqRelRole inert_eq_rel
role = eqRelRole eq_rel
- downgraded_redn = downgradeRedn role inert_role redn
+ !downgraded_redn
+ | inert_role == Nominal && role == Representational
+ = mkSubRedn (mkTyConApp tc xis) redn
+ | otherwise
+ = redn
; _ ->
-- inerts didn't work. Try to reduce again, in STEP 4.
do { result3 <- try_to_reduce tc xis tc_rewriters
; case result3 of
- Just redn -> finish True (homogenise redn)
+ Just redn -> finish RewroteArgsAddToCache (homogenise redn)
-- we have made no progress at all: STEP 5 (GIVEUP).
_ -> return give_up }}}}}
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?
- -- Precondition: True ==> input coercion has
- -- no coercion holes
- -> Reduction -> RewriteM Reduction
+ finish :: AddToCache -- Add to the cache?
+ -> Reduction -- Precondition: we can only add to the cache a 'Reduction'
+ -- which does not have any coercion holes.
+ -> RewriteM Reduction
finish use_cache redn
= do { -- rewrite the result: FINISH 1
final_redn <- rewrite_reduction redn
- ; eq_rel <- getEqRel
-
+ ; case use_cache of
+ { Don'tAddToCache {} -> return final_redn
+ ; RewroteArgsAddToCache ->
-- extend the cache: FINISH 2
- ; when (use_cache && eq_rel == NomEq) $
- -- the cache only wants Nominal eqs
- liftTcS $ extendFamAppCache tc tys final_redn
- ; return final_redn }
+ do { eq_rel <- getEqRel
+ ; when (eq_rel == NomEq) $
+ -- the cache only wants Nominal eqs
+ liftTcS $ extendFamAppCache tc tys final_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.
+ ; return final_redn } } }
{-# INLINE finish #-}
+-- | How to finish rewriting an exact type family application,
+-- depending on whether we have rewritten the arguments or not.
+data AddToCache
+ -- | We didn't rewrite the arguments: don't add to the cache.
+ --
+ -- See Note [rewrite_exact_fam_app performance].
+ = Don'tAddToCache
+ -- | We rewrote the arguments. We add the type family application,
+ -- with rewritten arguments, to the cache.
+ | RewroteArgsAddToCache
+
-- Returned coercion is input ~r output, where r is the role in the RewriteM monad
-- See Note [How to normalise a family application]
try_to_reduce :: TyCon -> [TcType] -> [TcPluginRewriter]
@@ -894,23 +912,23 @@ try_to_reduce tc tys tc_rewriters
[ runTcPluginRewriters rewrite_env tc_rewriters tys -- STEP 1a & STEP 4a
, lookupFamAppCache tc tys -- STEP 1b & STEP 4b
, matchFam tc tys ] -- STEP 1c & STEP 4c
- ; traverse downgrade result }
+ ; traverse finish result }
where
-- The result above is always Nominal. We might want a Representational
-- coercion; this downgrades (and prints, out of convenience).
- downgrade :: Reduction -> RewriteM Reduction
- downgrade redn
+ finish :: Reduction -> RewriteM Reduction
+ finish redn
= do { traceRewriteM "Eager T.F. reduction success" $
- vcat [ ppr tc
- , ppr tys
- , ppr redn
- ]
+ vcat [ ppr tc
+ , ppr tys
+ , ppr redn
+ ]
; eq_rel <- getEqRel
-- manually doing it this way avoids allocation in the vastly
-- common NomEq case
; case eq_rel of
NomEq -> return redn
- ReprEq -> return $ mkSubRedn redn }
+ ReprEq -> return $ mkSubRedn (mkTyConApp tc tys) redn }
-- Retrieve all type-checking plugins that can rewrite a (saturated) type-family application
-- headed by the given 'TyCon`.
@@ -959,11 +977,17 @@ runTcPluginRewriters rewriteEnv rewriterFunctions tys
-- | The result of rewriting a tyvar "one step".
data RewriteTvResult
= RTRNotFollowed
- -- ^ The inert set doesn't make the tyvar equal to anything else
+ -- ^ Not a filled metavariable, and the inert set doesn't make
+ -- the tyvar equal to anything else.
- | RTRFollowed !Reduction
- -- ^ The tyvar rewrites to a not-necessarily rewritten other type.
- -- The role is determined by the RewriteEnv.
+ | RTRFollowedMeta !TcType
+ -- ^ We followed a filled metavariable to the given type,
+ -- which has not yet been rewritten.
+
+ | RTRFollowedInert !Reduction
+ -- ^ The tyvar rewrites to a not-necessarily rewritten other type,
+ -- using an inert equality; this rewriting is stored in a
+ -- 'Reduction'.
--
-- With Quick Look, the returned TcType can be a polytype;
-- that is, in the constraint solver, a unification variable
@@ -974,14 +998,13 @@ rewriteTyVar :: TyVar -> RewriteM Reduction
rewriteTyVar tv
= do { mb_yes <- rewrite_tyvar1 tv
; case mb_yes of
- RTRFollowed redn -> rewrite_reduction redn
-
+ RTRFollowedMeta ty -> rewrite_one ty
+ RTRFollowedInert redn -> rewrite_reduction redn
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 $ mkReflRedn role ty' } }
+ ; return $ mkReflRedn ty' } }
rewrite_tyvar1 :: TcTyVar -> RewriteM RewriteTvResult
-- "Rewriting" a type variable means to apply the substitution to it
@@ -995,9 +1018,7 @@ rewrite_tyvar1 tv
; case mb_ty of
Just ty -> do { traceRewriteM "Following filled tyvar"
(ppr tv <+> equals <+> ppr ty)
- ; role <- getRole
- ; return $ RTRFollowed $
- mkReflRedn role ty }
+ ; return $ RTRFollowedMeta ty }
Nothing -> do { traceRewriteM "Unfilled tyvar" (pprTyVar tv)
; fr <- getFlavourRole
; rewrite_tyvar2 tv fr } }
@@ -1022,20 +1043,21 @@ rewrite_tyvar2 tv fr@(_, eq_rel)
, text "wanted_rewrite_wanted:" <+> ppr wrw ]
; when wrw $ recordRewriter ctev
- ; let rewriting_co1 = ctEvCoercion ctev
- rewriting_co = case (ct_eq_rel, eq_rel) of
+ ; let rewriting_dco1 = mkDehydrateCo $ ctEvCoercion ctev
+ rewriting_dco = 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
+ rewriting_dco1
+ (NomEq, NomEq) -> rewriting_dco1
+ (NomEq, ReprEq) -> mkSubDCo lhs_ty rewriting_dco1 rhs_ty
- ; return $ RTRFollowed $ mkReduction rewriting_co rhs_ty }
+ ; return $ RTRFollowedInert $ mkReduction rewriting_dco rhs_ty }
_other -> return RTRNotFollowed }
-
where
+ lhs_ty :: TcType
+ lhs_ty = mkTyVarTy tv
can_rewrite :: EqCt -> Bool
can_rewrite ct = eqCtFlavourRole ct `eqCanRewriteFR` fr
-- This is THE key call of eqCanRewriteFR
diff --git a/compiler/GHC/Tc/TyCl/Utils.hs b/compiler/GHC/Tc/TyCl/Utils.hs
index 8e7b3b8c39..56d6710872 100644
--- a/compiler/GHC/Tc/TyCl/Utils.hs
+++ b/compiler/GHC/Tc/TyCl/Utils.hs
@@ -40,7 +40,7 @@ import GHC.Builtin.Uniques ( mkBuiltinUnique )
import GHC.Hs
-import GHC.Core.TyCo.Rep( Type(..), Coercion(..), MCoercion(..), UnivCoProvenance(..) )
+import GHC.Core.TyCo.Rep( Type(..), Coercion(..), MCoercion(..), DCoercion(..), UnivCoProvenance(..) )
import GHC.Core.Multiplicity
import GHC.Core.Predicate
import GHC.Core.Make( rEC_SEL_ERROR_ID )
@@ -143,7 +143,8 @@ synonymTyConsOfType ty
go_co (CoVarCo _) = emptyNameEnv
go_co (HoleCo {}) = emptyNameEnv
go_co (AxiomInstCo _ _ cs) = go_co_s cs
- go_co (UnivCo p _ ty ty') = go_prov p `plusNameEnv` go ty `plusNameEnv` go ty'
+ go_co (HydrateDCo _ ty dco _)= go ty `plusNameEnv` go_dco dco
+ go_co (UnivCo p _ ty ty') = go_prov go_co p `plusNameEnv` go ty `plusNameEnv` go ty'
go_co (SymCo co) = go_co co
go_co (TransCo co co') = go_co co `plusNameEnv` go_co co'
go_co (SelCo _ co) = go_co co
@@ -153,15 +154,30 @@ synonymTyConsOfType ty
go_co (SubCo co) = go_co co
go_co (AxiomRuleCo _ cs) = go_co_s cs
- go_prov (PhantomProv co) = go_co co
- go_prov (ProofIrrelProv co) = go_co co
- go_prov (PluginProv _) = emptyNameEnv
- go_prov (CorePrepProv _) = emptyNameEnv
+ go_dco ReflDCo = emptyNameEnv
+ go_dco (GReflRightDCo co) = go_co co
+ go_dco (GReflLeftDCo co) = go_co co
+ go_dco (TyConAppDCo cs) = go_dco_s cs
+ go_dco (AppDCo co co') = go_dco co `plusNameEnv` go_dco co'
+ go_dco (ForAllDCo _ dco dco') = go_dco dco `plusNameEnv` go_dco dco'
+ go_dco (CoVarDCo _) = emptyNameEnv
+ go_dco AxiomInstDCo{} = emptyNameEnv
+ go_dco StepsDCo{} = emptyNameEnv
+ go_dco (TransDCo co1 co2) = go_dco co1 `plusNameEnv` go_dco co2
+ go_dco (DehydrateCo co) = go_co co
+ go_dco (UnivDCo prov rhs) = go_prov go_dco prov `plusNameEnv` go rhs
+ go_dco (SubDCo dco) = go_dco dco
+
+ go_prov syns (PhantomProv co) = syns co
+ go_prov syns (ProofIrrelProv co) = syns co
+ go_prov _ (PluginProv _) = emptyNameEnv
+ go_prov _ (CorePrepProv _) = emptyNameEnv
go_tc tc | isTypeSynonymTyCon tc = unitNameEnv (tyConName tc) tc
| otherwise = emptyNameEnv
go_s tys = foldr (plusNameEnv . go) emptyNameEnv tys
go_co_s cos = foldr (plusNameEnv . go_co) emptyNameEnv cos
+ go_dco_s dcos = foldr (plusNameEnv . go_dco) emptyNameEnv dcos
-- | A monad for type synonym cycle checking, which keeps
-- track of the TyCons which are known to be acyclic, or
diff --git a/compiler/GHC/Tc/Types.hs b/compiler/GHC/Tc/Types.hs
index 28668b7979..033327912d 100644
--- a/compiler/GHC/Tc/Types.hs
+++ b/compiler/GHC/Tc/Types.hs
@@ -293,8 +293,8 @@ data RewriteEnv
-- ^ At what role are we rewriting?
--
-- See Note [Rewriter EqRels] in GHC.Tc.Solver.Rewrite
-
- , re_rewriters :: !(TcRef RewriterSet) -- ^ See Note [Wanteds rewrite Wanteds]
+ , re_rewriters :: !(TcRef RewriterSet)
+ -- ^ See Note [Wanteds rewrite Wanteds]
}
-- RewriteEnv is mostly used in @GHC.Tc.Solver.Rewrite@, but it is defined
-- here so that it can also be passed to rewriting plugins.
diff --git a/compiler/GHC/Tc/Types/Evidence.hs b/compiler/GHC/Tc/Types/Evidence.hs
index 4216613c4a..dbbe74faff 100644
--- a/compiler/GHC/Tc/Types/Evidence.hs
+++ b/compiler/GHC/Tc/Types/Evidence.hs
@@ -40,6 +40,7 @@ module GHC.Tc.Types.Evidence (
-- * TcCoercion
TcCoercion, TcCoercionR, TcCoercionN, TcCoercionP, CoercionHole,
TcMCoercion, TcMCoercionN, TcMCoercionR,
+ TcDCoercion,
Role(..), LeftOrRight(..), pickLR,
maybeSymCo,
unwrapIP, wrapIP,
@@ -109,6 +110,7 @@ type TcCoercionP = CoercionP -- a phantom coercion
type TcMCoercion = MCoercion
type TcMCoercionN = MCoercionN -- nominal
type TcMCoercionR = MCoercionR -- representational
+type TcDCoercion = DCoercion
-- | If a 'SwapFlag' is 'IsSwapped', flip the orientation of a coercion
maybeSymCo :: SwapFlag -> TcCoercion -> TcCoercion
diff --git a/compiler/GHC/Tc/Utils/TcMType.hs b/compiler/GHC/Tc/Utils/TcMType.hs
index 7db80cfccb..0e3e75ec38 100644
--- a/compiler/GHC/Tc/Utils/TcMType.hs
+++ b/compiler/GHC/Tc/Utils/TcMType.hs
@@ -79,7 +79,7 @@ module GHC.Tc.Utils.TcMType (
zonkTyCoVarsAndFV, zonkTcTypeAndFV, zonkDTyCoVarSetAndFV,
zonkTyCoVarsAndFVList,
- zonkTcType, zonkTcTypes, zonkCo,
+ zonkTcType, zonkTcTypes, zonkCo, zonkCtEvidence,
zonkTyCoVarKind,
zonkEvVar, zonkWC, zonkImplication, zonkSimples,
zonkId, zonkCoVar,
@@ -404,7 +404,7 @@ unpackCoercionHole_maybe (CoercionHole { ch_ref = ref }) = readTcRef ref
-- itself is needed only for printing.)
-- Always returns the checked coercion, but this return value is necessary
-- so that the input coercion is forced only when the output is forced.
-checkCoercionHole :: CoVar -> Coercion -> TcM Coercion
+checkCoercionHole :: HasDebugCallStack => CoVar -> Coercion -> TcM Coercion
checkCoercionHole cv co
| debugIsOn
= do { cv_ty <- zonkTcType (varType cv)
@@ -412,8 +412,10 @@ checkCoercionHole cv co
; return $
assertPpr (ok cv_ty)
(text "Bad coercion hole" <+>
- ppr cv <> colon <+> vcat [ ppr t1, ppr t2, ppr role
- , ppr cv_ty ])
+ ppr cv <> colon <+> vcat [ text "t1:" <+> ppr t1
+ , text "t2:" <+> ppr t2
+ , text "role:" <+> ppr role
+ , text "cv_ty:" <+> ppr cv_ty ])
co }
| otherwise
= return co
@@ -1542,26 +1544,43 @@ collect_cand_qtvs_co :: TcType -- original type at top of recursion; for errors
-> VarSet -- bound variables
-> CandidatesQTvs -> Coercion
-> TcM CandidatesQTvs
-collect_cand_qtvs_co orig_ty cur_lvl bound = go_co
+collect_cand_qtvs_co orig_ty cur_lvl bound dv = fst $ collect_cand_qtvs_co_dco orig_ty cur_lvl bound dv
+
+collect_cand_qtvs_dco :: TcType -- original type at top of recursion; for errors
+ -> TcLevel
+ -> VarSet -- bound variables
+ -> CandidatesQTvs -> DCoercion
+ -> TcM CandidatesQTvs
+collect_cand_qtvs_dco orig_ty cur_lvl bound dv = snd $ collect_cand_qtvs_co_dco orig_ty cur_lvl bound dv
+
+collect_cand_qtvs_co_dco :: TcType -- original type at top of recursion; for errors
+ -> TcLevel
+ -> VarSet -- bound variables
+ -> CandidatesQTvs
+ -> (Coercion -> TcM CandidatesQTvs, DCoercion -> TcM CandidatesQTvs)
+collect_cand_qtvs_co_dco orig_ty cur_lvl bound dv = (go_co dv, go_dco dv)
where
- go_co dv (Refl ty) = collect_cand_qtvs orig_ty True cur_lvl bound dv ty
- go_co dv (GRefl _ ty mco) = do { dv1 <- collect_cand_qtvs orig_ty True cur_lvl bound dv ty
- ; go_mco dv1 mco }
- go_co dv (TyConAppCo _ _ cos) = foldlM go_co dv cos
- go_co dv (AppCo co1 co2) = foldlM go_co dv [co1, co2]
+ go_co :: CandidatesQTvs -> Coercion -> TcM CandidatesQTvs
+ go_co dv (Refl ty) = collect_cand_qtvs orig_ty True cur_lvl bound dv ty
+ go_co dv (GRefl _ ty mco) = do dv1 <- collect_cand_qtvs orig_ty True cur_lvl bound dv ty
+ go_mco dv1 mco
+ go_co dv (TyConAppCo _ _ cos) = foldlM go_co dv cos
+ go_co dv (AppCo co1 co2) = foldlM go_co dv [co1, co2]
go_co dv (FunCo _ _ _ w co1 co2) = foldlM go_co dv [w, co1, co2]
- go_co dv (AxiomInstCo _ _ cos) = foldlM go_co dv cos
- go_co dv (AxiomRuleCo _ cos) = foldlM go_co dv cos
- go_co dv (UnivCo prov _ t1 t2) = do { dv1 <- go_prov dv prov
- ; dv2 <- collect_cand_qtvs orig_ty True cur_lvl bound dv1 t1
- ; collect_cand_qtvs orig_ty True cur_lvl bound dv2 t2 }
- go_co dv (SymCo co) = go_co dv co
- go_co dv (TransCo co1 co2) = foldlM go_co dv [co1, co2]
- go_co dv (SelCo _ co) = go_co dv co
- go_co dv (LRCo _ co) = go_co dv co
- go_co dv (InstCo co1 co2) = foldlM go_co dv [co1, co2]
- go_co dv (KindCo co) = go_co dv co
- go_co dv (SubCo co) = go_co dv co
+ go_co dv (AxiomInstCo _ _ cos) = foldlM go_co dv cos
+ go_co dv (AxiomRuleCo _ cos) = foldlM go_co dv cos
+ go_co dv (HydrateDCo _ t1 dco _) = do dv1 <- collect_cand_qtvs orig_ty True cur_lvl bound dv t1
+ go_dco dv1 dco
+ go_co dv (UnivCo prov _ t1 t2) = do dv1 <- go_prov go_co dv prov
+ dv2 <- collect_cand_qtvs orig_ty True cur_lvl bound dv1 t1
+ collect_cand_qtvs orig_ty True cur_lvl bound dv2 t2
+ go_co dv (SymCo co) = go_co dv co
+ go_co dv (TransCo co1 co2) = foldlM go_co dv [co1, co2]
+ go_co dv (SelCo _ co) = go_co dv co
+ go_co dv (LRCo _ co) = go_co dv co
+ go_co dv (InstCo co1 co2) = foldlM go_co dv [co1, co2]
+ go_co dv (KindCo co) = go_co dv co
+ go_co dv (SubCo co) = go_co dv co
go_co dv (HoleCo hole)
= do m_co <- unpackCoercionHole_maybe hole
@@ -1575,13 +1594,33 @@ collect_cand_qtvs_co orig_ty cur_lvl bound = go_co
= do { dv1 <- go_co dv kind_co
; collect_cand_qtvs_co orig_ty cur_lvl (bound `extendVarSet` tcv) dv1 co }
+ go_dco :: CandidatesQTvs -> DCoercion -> TcM CandidatesQTvs
+ go_dco dv ReflDCo = return dv
+ go_dco dv (GReflRightDCo co) = go_co dv co
+ go_dco dv (GReflLeftDCo co) = go_co dv co
+ go_dco dv (TyConAppDCo cos) = foldlM go_dco dv cos
+ go_dco dv (AppDCo co1 co2) = foldlM go_dco dv [co1, co2]
+ go_dco dv AxiomInstDCo{} = return dv
+ go_dco dv StepsDCo{} = return dv
+ go_dco dv (TransDCo co1 co2) = foldlM go_dco dv [co1, co2]
+ go_dco dv (CoVarDCo cv) = go_cv dv cv
+
+ go_dco dv (ForAllDCo tcv kind_dco co)
+ = do { dv1 <- go_dco dv kind_dco
+ ; collect_cand_qtvs_dco orig_ty cur_lvl (bound `extendVarSet` tcv) dv1 co }
+
+ go_dco dv (DehydrateCo co) = go_co dv co
+ go_dco dv (UnivDCo prov rhs) = do dv1 <- go_prov go_dco dv prov
+ collect_cand_qtvs orig_ty True cur_lvl bound dv1 rhs
+ go_dco dv (SubDCo dco) = go_dco dv dco
+
go_mco dv MRefl = return dv
go_mco dv (MCo co) = go_co dv co
- go_prov dv (PhantomProv co) = go_co dv co
- go_prov dv (ProofIrrelProv co) = go_co dv co
- go_prov dv (PluginProv _) = return dv
- go_prov dv (CorePrepProv _) = return dv
+ go_prov collect dv (PhantomProv co) = collect dv co
+ go_prov collect dv (ProofIrrelProv co) = collect dv co
+ go_prov _ dv (PluginProv _) = return dv
+ go_prov _ dv (CorePrepProv _) = return dv
go_cv :: CandidatesQTvs -> CoVar -> TcM CandidatesQTvs
go_cv dv@(DV { dv_cvs = cvs }) cv
@@ -2831,7 +2870,7 @@ zonkRewriterSet (RewriterSet set)
check_ty :: Type -> UnfilledCoercionHoleMonoid
check_co :: Coercion -> UnfilledCoercionHoleMonoid
- (check_ty, _, check_co, _) = foldTyCo folder ()
+ (check_ty, _, check_co, _, _, _) = foldTyCo folder ()
folder :: TyCoFolder () UnfilledCoercionHoleMonoid
folder = TyCoFolder { tcf_view = noView
diff --git a/compiler/GHC/Tc/Utils/TcType.hs b/compiler/GHC/Tc/Utils/TcType.hs
index ae25678600..223caa961c 100644
--- a/compiler/GHC/Tc/Utils/TcType.hs
+++ b/compiler/GHC/Tc/Utils/TcType.hs
@@ -1088,7 +1088,7 @@ exactTyCoVarsOfTypes tys = runTyCoVars (exact_tys tys)
exact_ty :: Type -> Endo TyCoVarSet
exact_tys :: [Type] -> Endo TyCoVarSet
-(exact_ty, exact_tys, _, _) = foldTyCo exactTcvFolder emptyVarSet
+(exact_ty, exact_tys, _, _, _, _) = foldTyCo exactTcvFolder emptyVarSet
exactTcvFolder :: TyCoFolder TyCoVarSet (Endo TyCoVarSet)
exactTcvFolder = deepTcvFolder { tcf_view = coreView }
diff --git a/compiler/GHC/Tc/Utils/Unify.hs b/compiler/GHC/Tc/Utils/Unify.hs
index 428eba5d69..c030993b6c 100644
--- a/compiler/GHC/Tc/Utils/Unify.hs
+++ b/compiler/GHC/Tc/Utils/Unify.hs
@@ -2685,7 +2685,7 @@ mkOccFolders :: TcTyVar -> (TcType -> Bool, TcCoercion -> Bool)
-- No expansion of type synonyms
mkOccFolders lhs_tv = (getAny . check_ty, getAny . check_co)
where
- !(check_ty, _, check_co, _) = foldTyCo occ_folder emptyVarSet
+ !(check_ty, _, check_co, _, _check_dco, _) = foldTyCo occ_folder emptyVarSet
occ_folder = TyCoFolder { tcf_view = noView -- Don't expand synonyms
, tcf_tyvar = do_tcv, tcf_covar = do_tcv
, tcf_hole = do_hole
@@ -2786,7 +2786,7 @@ pprPur (PuFail prob) = text "PuFail:" <> ppr prob
pprPur (PuOK {}) = text "PuOK"
okCheckRefl :: TcType -> TcM (PuResult a Reduction)
-okCheckRefl ty = return (PuOK emptyBag (mkReflRedn Nominal ty))
+okCheckRefl ty = return (PuOK emptyBag (mkReflRedn ty))
failCheckWith :: CheckTyEqResult -> TcM (PuResult a b)
failCheckWith p = return (PuFail p)
@@ -2898,9 +2898,12 @@ checkTyEqRhs flags ty
-> failCheckWith impredicativeProblem -- Not allowed (TyEq:F)
| otherwise
-> do { w_res <- checkTyEqRhs flags w
+ -- ; a_rep <- fmap reductionDCoercion <$> checkTyEqRhs flags (getRuntimeRep a)
; a_res <- checkTyEqRhs flags a
+ -- ; r_rep <- fmap reductionDCoercion <$> checkTyEqRhs flags (getRuntimeRep r)
; r_res <- checkTyEqRhs flags r
- ; return (mkFunRedn Nominal af <$> w_res <*> a_res <*> r_res) }
+ ; return (mkFunRedn af <$> w_res <*> pure ReflDCo <*> pure ReflDCo <*> a_res <*> r_res) }
+ -- SLD TODO not sure about this
AppTy fun arg -> do { fun_res <- checkTyEqRhs flags fun
; arg_res <- checkTyEqRhs flags arg
@@ -2908,10 +2911,10 @@ checkTyEqRhs flags ty
CastTy ty co -> do { ty_res <- checkTyEqRhs flags ty
; co_res <- checkCo flags co
- ; return (mkCastRedn1 Nominal ty <$> co_res <*> ty_res) }
+ ; return (mkCastRedn1 <$> co_res <*> ty_res) }
CoercionTy co -> do { co_res <- checkCo flags co
- ; return (mkReflCoRedn Nominal <$> co_res) }
+ ; return (mkReflCoRedn <$> co_res) }
ForAllTy {}
| tef_foralls flags -> okCheckRefl ty
@@ -3095,7 +3098,7 @@ checkTyConApp flags@(TEF { tef_unifying = unifying, tef_foralls = foralls_ok })
recurseIntoTyConApp :: TyEqFlags a -> TyCon -> [TcType] -> TcM (PuResult a Reduction)
recurseIntoTyConApp flags tc tys
= do { tys_res <- mapCheck (checkTyEqRhs flags) tys
- ; return (mkTyConAppRedn Nominal tc <$> tys_res) }
+ ; return (mkTyConAppRedn tc <$> tys_res) }
-------------------
checkFamApp :: TyEqFlags a
@@ -3123,12 +3126,12 @@ checkFamApp flags@(TEF { tef_unifying = unifying, tef_occurs = occ_prob
TEFA_Recurse
-> do { tys_res <- mapCheck (checkTyEqRhs arg_flags) tys
; traceTc "under" (ppr tc $$ pprPur tys_res $$ ppr flags)
- ; return (mkTyConAppRedn Nominal tc <$> tys_res) }
+ ; return (mkTyConAppRedn tc <$> tys_res) }
TEFA_Break breaker -- Recurse; and break if there is a problem
-> do { tys_res <- mapCheck (checkTyEqRhs arg_flags) tys
; case tys_res of
- PuOK cts redns -> return (PuOK cts (mkTyConAppRedn Nominal tc redns))
+ PuOK cts redns -> return (PuOK cts (mkTyConAppRedn tc redns))
PuFail {} -> breaker fam_app }
where
arg_flags = famAppArgFlags flags
diff --git a/compiler/GHC/Types/Id/Make.hs b/compiler/GHC/Types/Id/Make.hs
index d50e3a52ec..f4122241e6 100644
--- a/compiler/GHC/Types/Id/Make.hs
+++ b/compiler/GHC/Types/Id/Make.hs
@@ -1048,9 +1048,9 @@ dataConSrcToImplBang bang_opts fam_envs arg_ty
= HsLazy -- For !Int#, say, use HsLazy
-- See Note [Data con wrappers and unlifted types]
- | let mb_co = topNormaliseType_maybe fam_envs (scaledThing arg_ty)
+ | let mb_redn= topNormaliseType_maybe fam_envs (scaledThing arg_ty)
-- Unwrap type families and newtypes
- arg_ty' = case mb_co of
+ arg_ty' = case mb_redn of
{ Just redn -> scaledSet arg_ty (reductionReducedType redn)
; Nothing -> arg_ty }
, all (not . isNewTyCon . fst) (splitTyConApp_maybe $ scaledThing arg_ty')
@@ -1058,9 +1058,10 @@ dataConSrcToImplBang bang_opts fam_envs arg_ty
= if bang_opt_unbox_disable bang_opts
then HsStrict True -- Not unpacking because of -O0
-- See Note [Detecting useless UNPACK pragmas] in GHC.Core.DataCon
- else case mb_co of
+ else case mb_redn of
Nothing -> HsUnpack Nothing
- Just redn -> HsUnpack (Just $ reductionCoercion redn)
+ Just redn -> HsUnpack $ Just $
+ mkHydrateReductionDCoercion Representational (scaledThing arg_ty) redn
| otherwise -- Record the strict-but-no-unpack decision
= HsStrict False
diff --git a/docs/users_guide/using-optimisation.rst b/docs/users_guide/using-optimisation.rst
index c75d5e6097..f492f87af8 100644
--- a/docs/users_guide/using-optimisation.rst
+++ b/docs/users_guide/using-optimisation.rst
@@ -813,6 +813,21 @@ as such you shouldn't need to set any of them explicitly. A flag
Turn off the coercion optimiser.
+.. ghc-flag:: -fkeep-dcoercions
+ :shortdesc: Keep directed coercions in the coercion optimiser
+ :type: dynamic
+ :category:
+
+ :default: Don't keep directed coercions in the coercion optimiser.
+
+ Keep directed coercions in the coercion optimiser, instead of turning
+ them into coercions. Only applies when coercion optimisation is enabled.
+ Turning this flag on will ensure that coercions borne from type family reduction
+ remain small, but it means the coercion optimiser is less able to optimise them.
+ If your program crucially relies on coercion optimisation
+ (i.e. enabling :ghc-flag:`-fno-opt-coercion` causes a significant regression in compile-time),
+ then you might want to NOT enable this flag.
+
.. ghc-flag:: -fno-pre-inlining
:shortdesc: Turn off pre-inlining
:type: dynamic
diff --git a/testsuite/tests/dcoercion/DCo_Array.hs b/testsuite/tests/dcoercion/DCo_Array.hs
new file mode 100644
index 0000000000..cf3e389a09
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Array.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE
+ MagicHash
+ , UnboxedTuples
+ , UnliftedFFITypes
+ #-}
+
+module DCo_Array where
+
+import DCo_Array_aux
+ ( memcpy_thaw )
+
+import GHC.Exts
+ ( Ptr, Int(I#), RealWorld
+ , MutableByteArray#, ByteArray#
+ , newByteArray#
+ )
+import GHC.IO ( IO(..) )
+import GHC.ST ( ST(..) )
+
+data UArray e = UArray !Int ByteArray#
+data STUArray s e = STUArray !Int (MutableByteArray# s)
+
+thawSTUArray :: UArray e -> ST RealWorld (STUArray RealWorld e)
+thawSTUArray (UArray n@(I# n#) arr#) = ST $ \s1# ->
+ case newByteArray# n# s1# of
+ (# s2#, marr# #) ->
+ case memcpy_thaw marr# arr# (fromIntegral n) of
+ IO m ->
+ case m s2# of
+ (# s3#, _ #) ->
+ (# s3#, STUArray n marr# #)
diff --git a/testsuite/tests/dcoercion/DCo_Array_aux.hs b/testsuite/tests/dcoercion/DCo_Array_aux.hs
new file mode 100644
index 0000000000..dbaf804349
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Array_aux.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE
+ DerivingStrategies
+ , MagicHash
+ , UnliftedFFITypes
+ #-}
+
+module DCo_Array_aux
+ ( memcpy_thaw ) where
+
+import Data.Word
+ ( Word32 )
+import GHC.Exts
+ ( MutableByteArray#, ByteArray#
+ , Ptr
+ )
+
+newtype CSize = CSize Word32
+ deriving newtype Num
+
+foreign import ccall unsafe "memcpy"
+ memcpy_thaw :: MutableByteArray# s -> ByteArray# -> CSize -> IO (Ptr a)
diff --git a/testsuite/tests/dcoercion/DCo_Coercion.hs b/testsuite/tests/dcoercion/DCo_Coercion.hs
new file mode 100644
index 0000000000..7a9d697c1a
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Coercion.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE PolyKinds #-}
+
+module DCo_Coercion where
+
+import Data.Type.Equality
+ ( (:~:)(..), (:~~:)(..) )
+import Data.Coerce
+ ( Coercible, coerce )
+
+data Coercion a b where
+ Coercion :: Coercible a b => Coercion a b
+
+class TestCoercion f where
+ testCoercion :: f a -> f b -> Maybe (Coercion a b)
+
+instance TestCoercion ((:~:) a) where
+ testCoercion Refl Refl = Just Coercion
+
+instance TestCoercion ((:~~:) a) where
+ testCoercion HRefl HRefl = Just Coercion
diff --git a/testsuite/tests/dcoercion/DCo_Hetero.hs b/testsuite/tests/dcoercion/DCo_Hetero.hs
new file mode 100644
index 0000000000..fb1e479a96
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Hetero.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE StandaloneKindSignatures #-}
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module DCo_Hetero where
+
+import GHC.Enum
+import GHC.Base
+
+type (:~~:) :: k1 -> k2 -> Type
+data a :~~: b where
+ HRefl :: a :~~: a
+
+instance a ~~ b => Enum (a :~~: b) where
+ toEnum = error "toEnum"
diff --git a/testsuite/tests/dcoercion/DCo_Hetero.stderr b/testsuite/tests/dcoercion/DCo_Hetero.stderr
index 887e81669b..2332767804 100644
--- a/testsuite/tests/dcoercion/DCo_Hetero.stderr
+++ b/testsuite/tests/dcoercion/DCo_Hetero.stderr
@@ -1,5 +1,9 @@
+<<<<<<< HEAD
DCo_Hetero.hs:24:10: warning: [GHC-06201] [-Wmissing-methods (in -Wdefault)]
+=======
+DCo_Hetero.hs:24:10: warning: [-Wmissing-methods (in -Wdefault)]
+>>>>>>> efc617419c (Directed coercions)
• No explicit implementation for
‘fromEnum’
• In the instance declaration for ‘Enum (a :~~: b)’
diff --git a/testsuite/tests/dcoercion/DCo_HsBinds.hs b/testsuite/tests/dcoercion/DCo_HsBinds.hs
new file mode 100644
index 0000000000..668231e067
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_HsBinds.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE GADTs #-}
+
+module DCo_HsBinds where
+
+import Prelude
+
+data GhcPass p where
+ GhcPs :: GhcPass Int
+ GhcRn :: GhcPass Float
+ GhcTc :: GhcPass Bool
+
+type HsPatSynDetails pass = [RecordPatSynField pass]
+data RecordPatSynField pass = RecordPatSynField ()
+
+-----------------------------------------
+
+class Outputable a where
+ methD :: a -> String
+
+instance Outputable (HsPatSynDetails (GhcPass r)) where
+ methD details = ppr_v =<< details
+ where
+ ppr_v v = case undefined :: GhcPass r of
+ GhcPs -> methD v
+ GhcRn -> methD v
+ GhcTc -> methD v
+
+instance Outputable (RecordPatSynField a) where
+ methD (RecordPatSynField v) = methD v
+
+instance Outputable () where
+ methD _ = "()"
diff --git a/testsuite/tests/dcoercion/DCo_HsType.hs b/testsuite/tests/dcoercion/DCo_HsType.hs
new file mode 100644
index 0000000000..067b5a8091
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_HsType.hs
@@ -0,0 +1,47 @@
+
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+
+module DCo_HsType ( hsWcScopedTvs ) where
+
+import Prelude (undefined)
+
+data GhcPass (c :: Pass)
+data Pass = Renamed | Typechecked
+type GhcRn = GhcPass 'Renamed
+
+data HsTyVarBndr pass
+type LHsTyVarBndr pass = XRec pass (HsTyVarBndr pass)
+
+type LHsSigType pass = XRec pass (HsSigType pass)
+type LHsSigWcType pass = HsWildCardBndrs pass (LHsSigType pass)
+
+type HsOuterSigTyVarBndrs = HsOuterTyVarBndrs
+data HsSigType pass
+ = HsSig { sig_bndrs :: HsOuterSigTyVarBndrs pass }
+
+data HsWildCardBndrs pass thing
+ = HsWC { hswc_body :: thing }
+
+data HsOuterTyVarBndrs pass
+
+type family NoGhcTc p
+type instance NoGhcTc (GhcPass pass) = GhcPass (NoGhcTcPass pass)
+
+type family NoGhcTcPass (p :: Pass) :: Pass where
+ NoGhcTcPass 'Typechecked = 'Renamed
+ NoGhcTcPass other = other
+
+type family XRec p a
+type instance XRec (GhcPass p) a = a
+
+hsOuterExplicitBndrs :: HsOuterTyVarBndrs (GhcPass p)
+ -> LHsTyVarBndr (NoGhcTc (GhcPass p))
+hsOuterExplicitBndrs = undefined
+
+hsWcScopedTvs :: LHsSigWcType GhcRn -> LHsTyVarBndr GhcRn
+hsWcScopedTvs sig_wc_ty
+ | HsWC { hswc_body = sig_ty } <- sig_wc_ty
+ , HsSig { sig_bndrs = outer_bndrs } <- sig_ty
+ = hsOuterExplicitBndrs outer_bndrs
diff --git a/testsuite/tests/dcoercion/DCo_InScope.hs b/testsuite/tests/dcoercion/DCo_InScope.hs
new file mode 100644
index 0000000000..083617f452
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_InScope.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE
+ ScopedTypeVariables
+ , DataKinds
+ , GADTs
+ , RankNTypes
+ , TypeOperators
+ , PolyKinds -- Comment out PolyKinds and the bug goes away.
+ #-}
+{-# OPTIONS_GHC -O #-}
+
+module DCo_InScope where
+
+import Data.Kind
+
+data AccValidation err a = AccFailure err | AccSuccess a
+
+data KeyValueError = MissingValue
+
+type WithKeyValueError = AccValidation [KeyValueError]
+
+missing :: forall f rs. RecApplicative rs -> Rec (WithKeyValueError :. f) rs
+missing (RecApplicative rpure) = rpure missingField
+ where
+ missingField :: forall x. (WithKeyValueError :. f) x
+ missingField = Compose $ AccFailure [MissingValue]
+
+data Rec :: (u -> Type) -> [u] -> Type where
+ RNil :: Rec f '[]
+ (:&) :: !(f r) -> !(Rec f rs) -> Rec f (r ': rs)
+
+newtype Compose (f :: l -> Type) (g :: k -> l) (x :: k)
+ = Compose { getCompose :: f (g x) }
+
+type (:.) f g = Compose f g
+
+newtype RecApplicative rs =
+ RecApplicative ( forall f. (forall x. f x) -> Rec f rs )
diff --git a/testsuite/tests/dcoercion/DCo_LiftTyped.hs b/testsuite/tests/dcoercion/DCo_LiftTyped.hs
new file mode 100644
index 0000000000..8b253743b4
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_LiftTyped.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE
+ DefaultSignatures
+ , MultiParamTypeClasses
+ , GADTs
+ , PolyKinds
+ , ScopedTypeVariables
+ , StandaloneKindSignatures
+#-}
+
+module ThSyntax where
+
+import Data.Kind
+import GHC.Exts
+
+type Code :: forall r. TYPE r -> Type
+data Code a = Code
+
+unTypeCode :: forall (r :: RuntimeRep) (a :: TYPE r) . Code a -> ()
+unTypeCode _ = ()
+
+type Lift :: forall r -> TYPE r -> Constraint
+class Lift r t where
+ lift :: t -> ()
+ default lift :: (r ~ ('BoxedRep 'Lifted)) => t -> ()
+ lift = unTypeCode . liftTyped
+ liftTyped :: t -> Code t
diff --git a/testsuite/tests/dcoercion/DCo_Phantom.hs b/testsuite/tests/dcoercion/DCo_Phantom.hs
new file mode 100644
index 0000000000..eceb3fc0ee
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Phantom.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeOperators, TypeFamilies,
+ UndecidableInstances, ConstraintKinds #-}
+module DCo_Phantom where
+
+import GHC.TypeLits as L
+import Data.Type.Bool
+
+
+-- We define a very simplistic O notation, with sufficient expressiveness
+-- to capture the complexity of a few simple sorting algorithms
+data AsympPoly = NLogN Nat Nat
+
+-- Synonyms for common terms
+type N = NLogN 1 0
+type LogN = NLogN 0 1
+type One = NLogN 0 0
+
+-- Just to be able to write it nicely
+type O (a :: AsympPoly) = a
+
+type family (^.) (n :: AsympPoly) (m :: Nat) :: AsympPoly where
+ (NLogN a b) ^. n = (NLogN (a L.* n) (b L.* n))
+
+infixl 7 ^.
+
+newtype Sorted (cpu :: AsympPoly) -- The minimum operational complexity
+ -- this algorithm satisfies.
+ (mem :: AsympPoly) -- The minimum space complexity this
+ -- algorithm satisfies.
+ (stable :: Bool) -- Whether the sort is stable or not.
+ a -- What was being sorted.
+ = Sorted {sortedBy :: [a]}
+
+mySortA :: Sorted (O(N^.2)) (O(N)) True Integer
+mySortA = _a [3,1,2]
diff --git a/testsuite/tests/dcoercion/DCo_PostProcess.hs b/testsuite/tests/dcoercion/DCo_PostProcess.hs
new file mode 100644
index 0000000000..ffd5ae8eeb
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_PostProcess.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE DataKinds #-}
+
+module DCo_PostProcess where
+
+data RdrName
+data SrcSpanAnnN
+
+type family Anno a
+type instance Anno RdrName = SrcSpanAnnN
+
+data Pass = Parsed
+data GhcPass (c :: Pass) where
+ GhcPs :: GhcPass 'Parsed
+type GhcPs = GhcPass 'Parsed
+
+type family IdP p
+type instance IdP (GhcPass p) = IdGhcP p
+type family IdGhcP pass where
+ IdGhcP 'Parsed = RdrName
+
+mkHsOpTy :: (Anno (IdGhcP p) ~ SrcSpanAnnN)
+ => IdP (GhcPass p) -> GhcPass p
+mkHsOpTy = mkHsOpTy
+
+mkLHsOpTy :: RdrName -> GhcPs
+mkLHsOpTy = mkHsOpTy
diff --git a/testsuite/tests/dcoercion/DCo_Specialise.hs b/testsuite/tests/dcoercion/DCo_Specialise.hs
new file mode 100644
index 0000000000..fde5bfb54f
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Specialise.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE Haskell2010 #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+module DCo_Specialise ( rnStmts1 ) where
+
+data RealWorld
+newtype M a = M (RealWorld -> a)
+fmapM :: (a -> b) -> M a -> M b
+fmapM f (M k) = M (f . k)
+
+data HsExpr
+data SrcSpanAnnA
+
+type family Anno a
+type instance Anno HsExpr = SrcSpanAnnA
+
+type AnnoBody body0_ = ( Anno body0_ ~ SrcSpanAnnA )
+
+rnStmts1 :: forall body1_ thing1_. AnnoBody body1_ => M (body1_, thing1_)
+rnStmts1 = rnStmts2 @body1_ @thing1_
+
+rnStmts2 :: forall body2_ thing2_. AnnoBody body2_ => M (body2_, thing2_)
+rnStmts2 = rnStmts3 @(body2_, thing2_)
+
+rnStmts3 :: M thing3_
+rnStmts3 = fmapM snd $ rnStmts1 @HsExpr
diff --git a/testsuite/tests/dcoercion/DCo_T15703_aux.hs b/testsuite/tests/dcoercion/DCo_T15703_aux.hs
new file mode 100644
index 0000000000..306a39e7a0
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_T15703_aux.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE InstanceSigs #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module DCo_T15703_aux where
+
+import Data.Kind
+import Data.Type.Equality
+import GHC.Generics
+
+data family Sing :: forall k. k -> Type
+data instance Sing :: forall i k c (p :: k). K1 i c p -> Type where
+ SK1 :: Sing x -> Sing ('K1 x)
+data instance Sing :: forall k i (c :: Meta) (f :: k -> Type) (p :: k).
+ M1 i c f p -> Type where
+ SM1 :: Sing x -> Sing ('M1 x)
+
+data instance Sing :: forall k (f :: k -> Type) (g :: k -> Type) (p :: k).
+ (f :*: g) p -> Type where
+ (:%*:) :: Sing x -> Sing y -> Sing (x ':*: y)
+
+data instance Sing :: forall p. Par1 p -> Type where
+ SPar1 :: Sing x -> Sing ('Par1 x)
+
+class PGeneric1 (f :: k -> Type) where
+ type From1 (z :: f a) :: Rep1 f a
+ type To1 (z :: Rep1 f a) :: f a
+
+class VGeneric1 (f :: k -> Type) where
+ sFot1 :: forall (a :: k) (r :: Rep1 f a). Sing r -> From1 (To1 r :: f a) :~: r
+
+instance PGeneric1 ((,) a) where
+ type From1 '(x, y) = 'M1 ('M1 ('M1 ('K1 x) ':*: 'M1 ('Par1 y)))
+ type To1 ('M1 ('M1 ('M1 ('K1 x) ':*: 'M1 ('Par1 y)))) = '(x, y)
+
+instance VGeneric1 ((,) a) where
+ sFot1 (SM1 (SM1 (SM1 SK1{} :%*: SM1 SPar1{}))) = Refl
diff --git a/testsuite/tests/dcoercion/DCo_TransOpt.hs b/testsuite/tests/dcoercion/DCo_TransOpt.hs
new file mode 100644
index 0000000000..e89ac6f113
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_TransOpt.hs
@@ -0,0 +1,108 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Main where
+
+-- base
+import Data.Foldable
+ ( for_ )
+
+-- ghc
+import GHC.Core.Coercion
+ ( DCoercion(ReflDCo, StepsDCo, TransDCo, TyConAppDCo)
+ , mkTransDCo
+ )
+
+-------------------------------------------------------------------------------
+
+main :: IO ()
+main =
+ for_ test_dcos \ ( nm, dco ) ->
+ case unreducedTopTransitivities dco of
+ Nothing -> do
+ putStrLn $ "OK: " ++ nm
+ putStrLn $ " " ++ show_dco dco
+ Just ( i, l, r ) -> do
+ putStrLn $ "FAIL: " ++ nm
+ putStrLn $ " " ++ show_dco dco
+ putStrLn $ " unreduced pair at index " ++ show i
+ putStrLn $ " LHS:" ++ show_dco l
+ putStrLn $ " RHS:" ++ show_dco r
+
+unreducedTopTransitivities
+ :: DCoercion -> Maybe ( Int, DCoercion, DCoercion )
+unreducedTopTransitivities
+ = check_reductions . top_trans
+
+top_trans :: DCoercion -> [ DCoercion ]
+top_trans = \case
+ ldco `TransDCo` rdco -> top_trans ldco ++ top_trans rdco
+ dco -> [dco]
+
+check_reductions :: [ DCoercion ] -> Maybe ( Int, DCoercion, DCoercion )
+check_reductions = go 0
+ where
+ go i ( dco1 : dco2 : dcos )
+ | should_cancel dco1 dco2
+ = Just ( i, dco1, dco2 )
+ | otherwise
+ = go (i+1) ( dco2 : dcos )
+ go _ _ = Nothing
+
+should_cancel :: DCoercion -> DCoercion -> Bool
+should_cancel ReflDCo _ = True
+should_cancel _ ReflDCo = True
+should_cancel (StepsDCo {}) (StepsDCo {}) = True
+should_cancel _ _ = False
+
+--------------------------------------------------------------------------------
+-- Handwritten directed coercions used for testing...
+
+-- Assume the LHS cannot be simplified further.
+test_lhs_dco_1 = ReflDCo
+test_lhs_dco_2 = StepsDCo 3
+test_lhs_dco_3 = TyConAppDCo [] `TransDCo` StepsDCo 3
+test_lhs_dco_4 = ( TyConAppDCo [] `TransDCo` TyConAppDCo [] ) `TransDCo` StepsDCo 3
+test_lhs_dco_5 = TyConAppDCo [] `TransDCo` ( TyConAppDCo [] `TransDCo` StepsDCo 3 )
+
+-- Don't make any such assumptions about the RHS.
+test_rhs_dco_1 = ReflDCo
+test_rhs_dco_2 = StepsDCo 3
+test_rhs_dco_3 = StepsDCo 3 `mkTransDCo` StepsDCo 10
+test_rhs_dco_4 = ReflDCo `mkTransDCo` TyConAppDCo []
+test_rhs_dco_5 = StepsDCo 4 `mkTransDCo` TyConAppDCo []
+test_rhs_dco_6 = ( ReflDCo `mkTransDCo` TyConAppDCo [] ) `mkTransDCo` TyConAppDCo []
+test_rhs_dco_7 = ( StepsDCo 4 `mkTransDCo` TyConAppDCo [] ) `mkTransDCo` TyConAppDCo []
+test_rhs_dco_8 = ReflDCo
+ `mkTransDCo` ( ReflDCo `mkTransDCo` StepsDCo 100 `mkTransDCo` ReflDCo )
+ `mkTransDCo` ReflDCo
+
+test_lhs_dcos :: [ ( String, DCoercion ) ]
+test_lhs_dcos = [ ( "lhs 1", test_lhs_dco_1 )
+ , ( "lhs 2", test_lhs_dco_2 )
+ , ( "lhs 3", test_lhs_dco_3 )
+ , ( "lhs 4", test_lhs_dco_4 )
+ , ( "lhs 5", test_lhs_dco_5 ) ]
+
+test_rhs_dcos :: [ ( String, DCoercion ) ]
+test_rhs_dcos = [ ( "rhs 1", test_rhs_dco_1 )
+ , ( "rhs 2", test_rhs_dco_2 )
+ , ( "rhs 3", test_rhs_dco_3 )
+ , ( "rhs 4", test_rhs_dco_4 )
+ , ( "rhs 5", test_rhs_dco_5 )
+ , ( "rhs 6", test_rhs_dco_6 )
+ , ( "rhs 7", test_rhs_dco_7 )
+ , ( "rhs 8", test_rhs_dco_8 )]
+
+test_dcos :: [ ( String, DCoercion ) ]
+test_dcos = [ ( l_nm ++ ", " ++ r_nm, lhs `mkTransDCo` rhs )
+ | (l_nm, lhs) <- test_lhs_dcos
+ , (r_nm, rhs) <- test_rhs_dcos ]
+
+show_dco :: DCoercion -> String
+show_dco = \case
+ ReflDCo -> "Refl"
+ StepsDCo n -> show n
+ TyConAppDCo {} -> "TC"
+ l `TransDCo` r -> show_dco l ++ " ; " ++ show_dco r
+ _ -> "???"
diff --git a/testsuite/tests/dcoercion/DCo_TransOpt.stdout b/testsuite/tests/dcoercion/DCo_TransOpt.stdout
new file mode 100644
index 0000000000..d2a85459ba
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_TransOpt.stdout
@@ -0,0 +1,80 @@
+OK: lhs 1, rhs 1
+ Refl
+OK: lhs 1, rhs 2
+ 3
+OK: lhs 1, rhs 3
+ 13
+OK: lhs 1, rhs 4
+ TC
+OK: lhs 1, rhs 5
+ 4 ; TC
+OK: lhs 1, rhs 6
+ TC ; TC
+OK: lhs 1, rhs 7
+ 4 ; TC ; TC
+OK: lhs 1, rhs 8
+ 100
+OK: lhs 2, rhs 1
+ 3
+OK: lhs 2, rhs 2
+ 6
+OK: lhs 2, rhs 3
+ 16
+OK: lhs 2, rhs 4
+ 3 ; TC
+OK: lhs 2, rhs 5
+ 7 ; TC
+OK: lhs 2, rhs 6
+ 3 ; TC ; TC
+OK: lhs 2, rhs 7
+ 7 ; TC ; TC
+OK: lhs 2, rhs 8
+ 103
+OK: lhs 3, rhs 1
+ TC ; 3
+OK: lhs 3, rhs 2
+ TC ; 6
+OK: lhs 3, rhs 3
+ TC ; 16
+OK: lhs 3, rhs 4
+ TC ; 3 ; TC
+OK: lhs 3, rhs 5
+ TC ; 7 ; TC
+OK: lhs 3, rhs 6
+ TC ; 3 ; TC ; TC
+OK: lhs 3, rhs 7
+ TC ; 7 ; TC ; TC
+OK: lhs 3, rhs 8
+ TC ; 103
+OK: lhs 4, rhs 1
+ TC ; TC ; 3
+OK: lhs 4, rhs 2
+ TC ; TC ; 6
+OK: lhs 4, rhs 3
+ TC ; TC ; 16
+OK: lhs 4, rhs 4
+ TC ; TC ; 3 ; TC
+OK: lhs 4, rhs 5
+ TC ; TC ; 7 ; TC
+OK: lhs 4, rhs 6
+ TC ; TC ; 3 ; TC ; TC
+OK: lhs 4, rhs 7
+ TC ; TC ; 7 ; TC ; TC
+OK: lhs 4, rhs 8
+ TC ; TC ; 103
+OK: lhs 5, rhs 1
+ TC ; TC ; 3
+OK: lhs 5, rhs 2
+ TC ; TC ; 6
+OK: lhs 5, rhs 3
+ TC ; TC ; 16
+OK: lhs 5, rhs 4
+ TC ; TC ; 3 ; TC
+OK: lhs 5, rhs 5
+ TC ; TC ; 7 ; TC
+OK: lhs 5, rhs 6
+ TC ; TC ; 3 ; TC ; TC
+OK: lhs 5, rhs 7
+ TC ; TC ; 7 ; TC ; TC
+OK: lhs 5, rhs 8
+ TC ; TC ; 103
diff --git a/testsuite/tests/dcoercion/DCo_TypeRep.hs b/testsuite/tests/dcoercion/DCo_TypeRep.hs
new file mode 100644
index 0000000000..161b449fde
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_TypeRep.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE GADTs, RankNTypes, PatternSynonyms, PolyKinds, ViewPatterns, TypeOperators #-}
+
+module DCo_TypeRep where
+
+import Type.Reflection (SomeTypeRep(SomeTypeRep), pattern Fun, typeRepKind )
+
+------------------------------------------------------------------------
+
+getSomeTypeRep :: SomeTypeRep
+getSomeTypeRep
+ | SomeTypeRep f <- getSomeTypeRep
+ = case typeRepKind f of
+ Fun _ _ -> error (show f)
+ _ -> error "not fun"
diff --git a/testsuite/tests/dcoercion/DCo_Typeable.hs b/testsuite/tests/dcoercion/DCo_Typeable.hs
new file mode 100644
index 0000000000..4470bafc17
--- /dev/null
+++ b/testsuite/tests/dcoercion/DCo_Typeable.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE LinearTypes #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneKindSignatures #-}
+
+module DCo_Typeable where
+
+import GHC.Prim ( TYPE )
+import GHC.Base ( Type, RuntimeRep(BoxedRep), Levity(Lifted), undefined )
+
+splitApp :: TypeRep a -> AppOrCon a
+splitApp TrType = IsApp trTYPE trLiftedRep
+ where
+ trTYPE :: TypeRep TYPE
+ trTYPE = undefined
+ trLiftedRep :: TypeRep ('BoxedRep 'Lifted)
+ trLiftedRep = undefined
+
+type TypeRep :: k -> Type
+data TypeRep (a :: k) where
+ TrType :: TypeRep Type
+
+data AppOrCon (a :: k) where
+ IsApp :: forall k k' (f :: k' -> k) (x :: k'). ()
+ => TypeRep f %1 -> TypeRep x %1 -> AppOrCon (f x)
diff --git a/testsuite/tests/dcoercion/Makefile b/testsuite/tests/dcoercion/Makefile
new file mode 100644
index 0000000000..9a36a1c5fe
--- /dev/null
+++ b/testsuite/tests/dcoercion/Makefile
@@ -0,0 +1,3 @@
+TOP=../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
diff --git a/testsuite/tests/dcoercion/all.T b/testsuite/tests/dcoercion/all.T
new file mode 100644
index 0000000000..63cd5f1a11
--- /dev/null
+++ b/testsuite/tests/dcoercion/all.T
@@ -0,0 +1,19 @@
+
+## Correctness tests: these programs should compile and pass Core Lint.
+test('DCo_Array', [extra_files(['DCo_Array_aux.hs'])], multimod_compile, ['DCo_Array.hs', '-v0 -O'])
+test('DCo_Coercion', normal, compile, ['-O'])
+test('DCo_Hetero', normal, compile, [''])
+test('DCo_HsBinds', normal, compile, ['-O'])
+test('DCo_HsType', normal, compile, [''])
+test('DCo_InScope', normal, compile, [''])
+test('DCo_LiftTyped', normal, compile, [''])
+test('DCo_Phantom', normal, compile, ['-fdefer-type-errors'])
+test('DCo_PostProcess', normal, compile, [''])
+test('DCo_Specialise', normal, compile, [''])
+test('DCo_T15703_aux', normal, compile, ['-O'])
+test('DCo_Typeable', normal, compile, ['-O'])
+test('DCo_TypeRep', normal, compile, [''])
+
+## The following tests that we optimise away certain transitive coercions.
+## However, this optimisation was too slow, so we comment this out for now.
+#test('DCo_TransOpt', normal, compile_and_run, ['-package ghc'])
diff --git a/testsuite/tests/dependent/should_compile/T14729.stderr b/testsuite/tests/dependent/should_compile/T14729.stderr
index 0aa6ad7f10..1a9a78dac7 100644
--- a/testsuite/tests/dependent/should_compile/T14729.stderr
+++ b/testsuite/tests/dependent/should_compile/T14729.stderr
@@ -1,5 +1,7 @@
TYPE SIGNATURES
- x :: forall (x :: Bool). P (F Int) (x |> Sym (T14729.D:R:FInt[0]))
+ x ::
+ forall (x :: Bool).
+ P (F Int) (x |> Sym (Hydrate nominal (F Int) T14729.D:R:FInt))
y :: forall {x :: Bool}. P Bool x
TYPE CONSTRUCTORS
type family F{1} :: * -> *
diff --git a/testsuite/tests/perf/compiler/all.T b/testsuite/tests/perf/compiler/all.T
index 14cd4cce94..d0c60235a0 100644
--- a/testsuite/tests/perf/compiler/all.T
+++ b/testsuite/tests/perf/compiler/all.T
@@ -171,7 +171,7 @@ test ('T8095',
[ only_ways(['normal']),
collect_compiler_stats('bytes allocated',2) ],
compile,
- ['-v0 -O'])
+ ['-v0 -O -fkeep-dcoercions'])
test ('T13386',
[ only_ways(['normal']),
collect_compiler_stats('bytes allocated',1) ],
@@ -208,7 +208,7 @@ test ('LargeRecord',
, extra_files(['SuperRecord.hs'])
],
multimod_compile,
- ['LargeRecord', '-v0 -O'])
+ ['LargeRecord', '-v0 -O -fkeep-dcoercions'])
test('T9961',
[ only_ways(['normal']),
@@ -245,7 +245,7 @@ test('T12227',
],
compile,
# Use `-M1G` to prevent memory thrashing with ghc-8.0.1.
- ['-O2 -ddump-hi -ddump-to-file +RTS -M1G'])
+ ['-O2 -fkeep-dcoercions -ddump-hi -ddump-to-file +RTS -M1G'])
test('T12425',
[ only_ways(['optasm']),
diff --git a/testsuite/tests/pmcheck/should_compile/T11195.hs b/testsuite/tests/pmcheck/should_compile/T11195.hs
index 7a7a4b05c5..b377eb2851 100644
--- a/testsuite/tests/pmcheck/should_compile/T11195.hs
+++ b/testsuite/tests/pmcheck/should_compile/T11195.hs
@@ -34,7 +34,7 @@ optForAllCoBndr = undefined
opt_trans :: InScopeSet -> NormalCo -> NormalCo -> NormalCo
opt_trans = undefined
-opt_univ :: LiftingContext -> SymFlag -> UnivCoProvenance -> Role
+opt_univ :: LiftingContext -> SymFlag -> UnivCoProvenance Coercion -> Role
-> Type -> Type -> Coercion
opt_univ = undefined
diff --git a/testsuite/tests/roles/should_compile/Roles13.stderr b/testsuite/tests/roles/should_compile/Roles13.stderr
index 92e8cace91..46119f59b9 100644
--- a/testsuite/tests/roles/should_compile/Roles13.stderr
+++ b/testsuite/tests/roles/should_compile/Roles13.stderr
@@ -1,20 +1,25 @@
==================== Tidy Core ====================
Result size of Tidy Core
- = {terms: 98, types: 38, coercions: 6, joins: 0/0}
+ = {terms: 98, types: 38, coercions: 8, joins: 0/0}
-- RHS size: {terms: 2, types: 2, coercions: 0, joins: 0/0}
convert1 :: Wrap Age -> Wrap Age
[GblId, Arity=1, Unf=OtherCon []]
convert1 = \ (ds :: Wrap Age) -> ds
--- RHS size: {terms: 1, types: 0, coercions: 6, joins: 0/0}
+-- RHS size: {terms: 1, types: 0, coercions: 8, joins: 0/0}
convert :: Wrap Age -> Int
[GblId, Arity=1, Unf=OtherCon []]
convert
= convert1
`cast` (<Wrap Age>_R
+<<<<<<< HEAD
%<Many>_N ->_R Roles13.N:Wrap[0] (Roles13.N:Age[0])
+=======
+ %<'Many>_N ->_R Roles13.N:Wrap[0] <Age>_R
+ ; Roles13.N:Age[0]
+>>>>>>> ea654ed05a (Directed coercions)
:: (Wrap Age -> Wrap Age) ~R# (Wrap Age -> Int))
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
@@ -69,8 +74,13 @@ Roles13.$tcAge :: GHC.Types.TyCon
[GblId, Unf=OtherCon []]
Roles13.$tcAge
= GHC.Types.TyCon
+<<<<<<< HEAD
3456257068627873222#Word64
14056710845110756026#Word64
+=======
+ 3456257068627873222##64
+ 14056710845110756026##64
+>>>>>>> ea654ed05a (Directed coercions)
Roles13.$trModule
$tcAge2
0#
@@ -103,8 +113,13 @@ Roles13.$tc'MkAge :: GHC.Types.TyCon
[GblId, Unf=OtherCon []]
Roles13.$tc'MkAge
= GHC.Types.TyCon
+<<<<<<< HEAD
18264039750958872441#Word64
1870189534242358050#Word64
+=======
+ 18264039750958872441##64
+ 1870189534242358050##64
+>>>>>>> ea654ed05a (Directed coercions)
Roles13.$trModule
$tc'MkAge2
0#
@@ -125,8 +140,13 @@ Roles13.$tcWrap :: GHC.Types.TyCon
[GblId, Unf=OtherCon []]
Roles13.$tcWrap
= GHC.Types.TyCon
+<<<<<<< HEAD
13773534096961634492#Word64
15591525585626702988#Word64
+=======
+ 13773534096961634492##64
+ 15591525585626702988##64
+>>>>>>> ea654ed05a (Directed coercions)
Roles13.$trModule
$tcWrap2
0#
@@ -164,8 +184,13 @@ Roles13.$tc'MkWrap :: GHC.Types.TyCon
[GblId, Unf=OtherCon []]
Roles13.$tc'MkWrap
= GHC.Types.TyCon
+<<<<<<< HEAD
15580677875333883466#Word64
808508687714473149#Word64
+=======
+ 15580677875333883466##64
+ 808508687714473149##64
+>>>>>>> ea654ed05a (Directed coercions)
Roles13.$trModule
$tc'MkWrap2
1#
diff --git a/testsuite/tests/tcplugins/RewritePlugin.hs b/testsuite/tests/tcplugins/RewritePlugin.hs
index 10aa7574a4..e6e8241b39 100644
--- a/testsuite/tests/tcplugins/RewritePlugin.hs
+++ b/testsuite/tests/tcplugins/RewritePlugin.hs
@@ -14,7 +14,7 @@ import GHC.Builtin.Types
import GHC.Core
( Expr(Coercion) )
import GHC.Core.Coercion
- ( Coercion, mkUnivCo )
+ ( DCoercion, mkUnivDCo )
import GHC.Core.Predicate
( EqRel(NomEq), Pred(EqPred)
, classifyPredType
@@ -82,8 +82,7 @@ rewriteAdd _ _ _ _ = pure TcPluginNoRewrite
mkTyFamReduction :: TyCon -> [ Type ] -> Type -> Reduction
-mkTyFamReduction tyCon args res = Reduction co res
+mkTyFamReduction tyCon args res = Reduction (mkTyConApp tyCon args) dco res
where
- co :: Coercion
- co = mkUnivCo ( PluginProv "RewritePlugin" ) Nominal
- ( mkTyConApp tyCon args ) res
+ dco :: DCoercion
+ dco = mkUnivDCo (PluginProv "RewritePlugin") res