summaryrefslogtreecommitdiff
path: root/compiler/GHC/Core/Reduction.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Core/Reduction.hs')
-rw-r--r--compiler/GHC/Core/Reduction.hs493
1 files changed, 275 insertions, 218 deletions
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)