-- (c) The University of Glasgow 2006

{-# LANGUAGE CPP #-}

module GHC.Core.Coercion.Opt
   ( optCoercion
   , checkAxInstCo
   , OptCoercionOpts (..)
   )
where

import GHC.Prelude

import GHC.Tc.Utils.TcType   ( exactTyCoVarsOfType )

import GHC.Core.TyCo.Rep
import GHC.Core.TyCo.Subst
import GHC.Core.Coercion
import GHC.Core.Type as Type hiding( substTyVarBndr, substTy )
import GHC.Core.TyCon
import GHC.Core.Coercion.Axiom
import GHC.Core.Unify

import GHC.Types.Var.Set
import GHC.Types.Var.Env

import GHC.Data.Pair
import GHC.Data.List.SetOps ( getNth )

import GHC.Utils.Outputable
import GHC.Utils.Constants (debugIsOn)
import GHC.Utils.Misc
import GHC.Utils.Panic
import GHC.Utils.Panic.Plain
import GHC.Utils.Trace

import Control.Monad   ( zipWithM )

{-
%************************************************************************
%*                                                                      *
                 Optimising coercions
%*                                                                      *
%************************************************************************

Note [Optimising coercion optimisation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Looking up a coercion's role or kind is linear in the size of the
coercion. Thus, doing this repeatedly during the recursive descent
of coercion optimisation is disastrous. We must be careful to avoid
doing this if at all possible.

Because it is generally easy to know a coercion's components' roles
from the role of the outer coercion, we pass down the known role of
the input in the algorithm below. We also keep functions opt_co2
and opt_co3 separate from opt_co4, so that the former two do Phantom
checks that opt_co4 can avoid. This is a big win because Phantom coercions
rarely appear within non-phantom coercions -- only in some TyConAppCos
and some AxiomInstCos. We handle these cases specially by calling
opt_co2.

Note [Optimising InstCo]
~~~~~~~~~~~~~~~~~~~~~~~~
(1) tv is a type variable
When we have (InstCo (ForAllCo tv h g) g2), we want to optimise.

Let's look at the typing rules.

h : k1 ~ k2
tv:k1 |- g : t1 ~ t2
-----------------------------
ForAllCo tv h g : (all tv:k1.t1) ~ (all tv:k2.t2[tv |-> tv |> sym h])

g1 : (all tv:k1.t1') ~ (all tv:k2.t2')
g2 : s1 ~ s2
--------------------
InstCo g1 g2 : t1'[tv |-> s1] ~ t2'[tv |-> s2]

We thus want some coercion proving this:

  (t1[tv |-> s1]) ~ (t2[tv |-> s2 |> sym h])

If we substitute the *type* tv for the *coercion*
(g2 ; t2 ~ t2 |> sym h) in g, we'll get this result exactly.
This is bizarre,
though, because we're substituting a type variable with a coercion. However,
this operation already exists: it's called *lifting*, and defined in GHC.Core.Coercion.
We just need to enhance the lifting operation to be able to deal with
an ambient substitution, which is why a LiftingContext stores a TCvSubst.

(2) cv is a coercion variable
Now consider we have (InstCo (ForAllCo cv h g) g2), we want to optimise.

h : (t1 ~r t2) ~N (t3 ~r t4)
cv : t1 ~r t2 |- g : t1' ~r2 t2'
n1 = nth r 2 (downgradeRole r N h) :: t1 ~r t3
n2 = nth r 3 (downgradeRole r N h) :: t2 ~r t4
------------------------------------------------
ForAllCo cv h g : (all cv:t1 ~r t2. t1') ~r2
                  (all cv:t3 ~r t4. t2'[cv |-> n1 ; cv ; sym n2])

g1 : (all cv:t1 ~r t2. t1') ~ (all cv: t3 ~r t4. t2')
g2 : h1 ~N h2
h1 : t1 ~r t2
h2 : t3 ~r t4
------------------------------------------------
InstCo g1 g2 : t1'[cv |-> h1] ~ t2'[cv |-> h2]

We thus want some coercion proving this:

  t1'[cv |-> h1] ~ t2'[cv |-> n1 ; h2; sym n2]

So we substitute the coercion variable c for the coercion
(h1 ~N (n1; h2; sym n2)) in g.
-}

-- | Coercion optimisation options
newtype OptCoercionOpts = OptCoercionOpts
   { OptCoercionOpts -> Bool
optCoercionEnabled :: Bool  -- ^ Enable coercion optimisation (reduce its size)
   }

optCoercion :: OptCoercionOpts -> TCvSubst -> Coercion -> NormalCo
-- ^ optCoercion applies a substitution to a coercion,
--   *and* optimises it to reduce its size
optCoercion :: OptCoercionOpts -> TCvSubst -> Coercion -> Coercion
optCoercion OptCoercionOpts
opts TCvSubst
env Coercion
co
  | OptCoercionOpts -> Bool
optCoercionEnabled OptCoercionOpts
opts = TCvSubst -> Coercion -> Coercion
optCoercion' TCvSubst
env Coercion
co
  | Bool
otherwise               = HasDebugCallStack => TCvSubst -> Coercion -> Coercion
substCo TCvSubst
env Coercion
co

optCoercion' :: TCvSubst -> Coercion -> NormalCo
optCoercion' :: TCvSubst -> Coercion -> Coercion
optCoercion' TCvSubst
env Coercion
co
  | Bool
debugIsOn
  = let out_co :: Coercion
out_co = LiftingContext -> Bool -> Coercion -> Coercion
opt_co1 LiftingContext
lc Bool
False Coercion
co
        (Pair Type
in_ty1  Type
in_ty2,  Role
in_role)  = Coercion -> (Pair Type, Role)
coercionKindRole Coercion
co
        (Pair Type
out_ty1 Type
out_ty2, Role
out_role) = Coercion -> (Pair Type, Role)
coercionKindRole Coercion
out_co
    in
    forall a. HasCallStack => Bool -> SDoc -> a -> a
assertPpr (TCvSubst -> Type -> Type
substTyUnchecked TCvSubst
env Type
in_ty1 Type -> Type -> Bool
`eqType` Type
out_ty1 Bool -> Bool -> Bool
&&
               TCvSubst -> Type -> Type
substTyUnchecked TCvSubst
env Type
in_ty2 Type -> Type -> Bool
`eqType` Type
out_ty2 Bool -> Bool -> Bool
&&
               Role
in_role forall a. Eq a => a -> a -> Bool
== Role
out_role)
              ( String -> SDoc
text String
"optCoercion changed types!"
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"in_co:") Int
2 (forall a. Outputable a => a -> SDoc
ppr Coercion
co)
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"in_ty1:") Int
2 (forall a. Outputable a => a -> SDoc
ppr Type
in_ty1)
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"in_ty2:") Int
2 (forall a. Outputable a => a -> SDoc
ppr Type
in_ty2)
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"out_co:") Int
2 (forall a. Outputable a => a -> SDoc
ppr Coercion
out_co)
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"out_ty1:") Int
2 (forall a. Outputable a => a -> SDoc
ppr Type
out_ty1)
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"out_ty2:") Int
2 (forall a. Outputable a => a -> SDoc
ppr Type
out_ty2)
                 SDoc -> SDoc -> SDoc
$$ SDoc -> Int -> SDoc -> SDoc
hang (String -> SDoc
text String
"subst:") Int
2 (forall a. Outputable a => a -> SDoc
ppr TCvSubst
env))
              Coercion
out_co

  | Bool
otherwise         = LiftingContext -> Bool -> Coercion -> Coercion
opt_co1 LiftingContext
lc Bool
False Coercion
co
  where
    lc :: LiftingContext
lc = TCvSubst -> LiftingContext
mkSubstLiftingContext TCvSubst
env


type NormalCo    = Coercion
  -- Invariants:
  --  * The substitution has been fully applied
  --  * For trans coercions (co1 `trans` co2)
  --       co1 is not a trans, and neither co1 nor co2 is identity

type NormalNonIdCo = NormalCo  -- Extra invariant: not the identity

-- | Do we apply a @sym@ to the result?
type SymFlag = Bool

-- | Do we force the result to be representational?
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
        -> SymFlag
        -> Coercion -> NormalCo
opt_co1 :: LiftingContext -> Bool -> Coercion -> Coercion
opt_co1 LiftingContext
env Bool
sym Coercion
co = LiftingContext -> Bool -> Role -> Coercion -> Coercion
opt_co2 LiftingContext
env Bool
sym (Coercion -> Role
coercionRole Coercion
co) Coercion
co

-- See Note [Optimising coercion optimisation]
-- | Optimize a coercion, knowing the coercion's role. No other assumptions.
opt_co2 :: LiftingContext
        -> SymFlag
        -> Role   -- ^ The role of the input coercion
        -> Coercion -> NormalCo
opt_co2 :: LiftingContext -> Bool -> Role -> Coercion -> Coercion
opt_co2 LiftingContext
env Bool
sym Role
Phantom Coercion
co = LiftingContext -> Bool -> Coercion -> Coercion
opt_phantom LiftingContext
env Bool
sym Coercion
co
opt_co2 LiftingContext
env Bool
sym Role
r       Coercion
co = LiftingContext
-> Bool -> Maybe Role -> Role -> Coercion -> Coercion
opt_co3 LiftingContext
env Bool
sym forall a. Maybe a
Nothing Role
r Coercion
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 :: LiftingContext
-> Bool -> Maybe Role -> Role -> Coercion -> Coercion
opt_co3 LiftingContext
env Bool
sym (Just Role
Phantom)          Role
_ Coercion
co = LiftingContext -> Bool -> Coercion -> Coercion
opt_phantom LiftingContext
env Bool
sym Coercion
co
opt_co3 LiftingContext
env Bool
sym (Just Role
Representational) Role
r Coercion
co = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
True  Role
r Coercion
co
  -- if mrole is Just Nominal, that can't be a downgrade, so we can ignore
opt_co3 LiftingContext
env Bool
sym Maybe Role
_                       Role
r Coercion
co = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
r Coercion
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 :: LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
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 :: LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4 LiftingContext
env Bool
_   Bool
rep Role
r (Refl Type
ty)
  = forall a. HasCallStack => Bool -> SDoc -> a -> a
assertPpr (Role
r forall a. Eq a => a -> a -> Bool
== Role
Nominal)
              (String -> SDoc
text String
"Expected role:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Role
r    SDoc -> SDoc -> SDoc
$$
               String -> SDoc
text String
"Found role:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Role
Nominal SDoc -> SDoc -> SDoc
$$
               String -> SDoc
text String
"Type:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Type
ty) forall a b. (a -> b) -> a -> b
$
    HasDebugCallStack => Role -> LiftingContext -> Type -> Coercion
liftCoSubst (Bool -> Role -> Role
chooseRole Bool
rep Role
r) LiftingContext
env Type
ty

opt_co4 LiftingContext
env Bool
_   Bool
rep Role
r (GRefl Role
_r Type
ty MCoercion
MRefl)
  = forall a. HasCallStack => Bool -> SDoc -> a -> a
assertPpr (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r)
              (String -> SDoc
text String
"Expected role:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Role
r SDoc -> SDoc -> SDoc
$$
               String -> SDoc
text String
"Found role:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Role
_r   SDoc -> SDoc -> SDoc
$$
               String -> SDoc
text String
"Type:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Type
ty) forall a b. (a -> b) -> a -> b
$
    HasDebugCallStack => Role -> LiftingContext -> Type -> Coercion
liftCoSubst (Bool -> Role -> Role
chooseRole Bool
rep Role
r) LiftingContext
env Type
ty

opt_co4 LiftingContext
env Bool
sym  Bool
rep Role
r (GRefl Role
_r Type
ty (MCo Coercion
co))
  = forall a. HasCallStack => Bool -> SDoc -> a -> a
assertPpr (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r)
              (String -> SDoc
text String
"Expected role:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Role
r SDoc -> SDoc -> SDoc
$$
               String -> SDoc
text String
"Found role:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Role
_r   SDoc -> SDoc -> SDoc
$$
               String -> SDoc
text String
"Type:" SDoc -> SDoc -> SDoc
<+> forall a. Outputable a => a -> SDoc
ppr Type
ty) forall a b. (a -> b) -> a -> b
$
    if Coercion -> Bool
isGReflCo Coercion
co Bool -> Bool -> Bool
|| Coercion -> Bool
isGReflCo Coercion
co'
    then HasDebugCallStack => Role -> LiftingContext -> Type -> Coercion
liftCoSubst Role
r' LiftingContext
env Type
ty
    else Bool -> Coercion -> Coercion
wrapSym Bool
sym forall a b. (a -> b) -> a -> b
$ Role -> Type -> Coercion -> Coercion -> Coercion
mkCoherenceRightCo Role
r' Type
ty' Coercion
co' (HasDebugCallStack => Role -> LiftingContext -> Type -> Coercion
liftCoSubst Role
r' LiftingContext
env Type
ty)
  where
    r' :: Role
r'  = Bool -> Role -> Role
chooseRole Bool
rep Role
r
    ty' :: Type
ty' = HasDebugCallStack => TCvSubst -> Type -> Type
substTy (LiftingContext -> TCvSubst
lcSubstLeft LiftingContext
env) Type
ty
    co' :: Coercion
co' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4 LiftingContext
env Bool
False Bool
False Role
Nominal Coercion
co

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (SymCo Coercion
co)  = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env (Bool -> Bool
not Bool
sym) Bool
rep Role
r Coercion
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 LiftingContext
env Bool
sym Bool
rep Role
r g :: Coercion
g@(TyConAppCo Role
_r TyCon
tc [Coercion]
cos)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r) forall a b. (a -> b) -> a -> b
$
    case (Bool
rep, Role
r) of
      (Bool
True, Role
Nominal) ->
        HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
Representational TyCon
tc
                     (forall a b c d. (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith3 (LiftingContext
-> Bool -> Maybe Role -> Role -> Coercion -> Coercion
opt_co3 LiftingContext
env Bool
sym)
                               (forall a b. (a -> b) -> [a] -> [b]
map forall a. a -> Maybe a
Just (TyCon -> [Role]
tyConRolesRepresentational TyCon
tc))
                               (forall a. a -> [a]
repeat Role
Nominal)
                               [Coercion]
cos)
      (Bool
False, Role
Nominal) ->
        HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
Nominal TyCon
tc (forall a b. (a -> b) -> [a] -> [b]
map (LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal) [Coercion]
cos)
      (Bool
_, Role
Representational) ->
                      -- must use opt_co2 here, because some roles may be P
                      -- See Note [Optimising coercion optimisation]
        HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
r TyCon
tc (forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (LiftingContext -> Bool -> Role -> Coercion -> Coercion
opt_co2 LiftingContext
env Bool
sym)
                                   (TyCon -> [Role]
tyConRolesRepresentational TyCon
tc)  -- the current roles
                                   [Coercion]
cos)
      (Bool
_, Role
Phantom) -> forall a. HasCallStack => String -> SDoc -> a
pprPanic String
"opt_co4 sees a phantom!" (forall a. Outputable a => a -> SDoc
ppr Coercion
g)

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (AppCo Coercion
co1 Coercion
co2)
  = Coercion -> Coercion -> Coercion
mkAppCo (LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r Coercion
co1)
            (LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
co2)

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (ForAllCo CoVar
tv Coercion
k_co Coercion
co)
  = case LiftingContext
-> Bool -> CoVar -> Coercion -> (LiftingContext, CoVar, Coercion)
optForAllCoBndr LiftingContext
env Bool
sym CoVar
tv Coercion
k_co of
      (LiftingContext
env', CoVar
tv', Coercion
k_co') -> CoVar -> Coercion -> Coercion -> Coercion
mkForAllCo CoVar
tv' Coercion
k_co' forall a b. (a -> b) -> a -> b
$
                            LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env' Bool
sym Bool
rep Role
r Coercion
co
     -- Use the "mk" functions to check for nested Refls

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (FunCo Role
_r Coercion
cow Coercion
co1 Coercion
co2)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r) forall a b. (a -> b) -> a -> b
$
    if Bool
rep
    then Role -> Coercion -> Coercion -> Coercion -> Coercion
mkFunCo Role
Representational Coercion
cow' Coercion
co1' Coercion
co2'
    else Role -> Coercion -> Coercion -> Coercion -> Coercion
mkFunCo Role
r Coercion
cow' Coercion
co1' Coercion
co2'
  where
    co1' :: Coercion
co1' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r Coercion
co1
    co2' :: Coercion
co2' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r Coercion
co2
    cow' :: Coercion
cow' = LiftingContext -> Bool -> Coercion -> Coercion
opt_co1 LiftingContext
env Bool
sym Coercion
cow

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (CoVarCo CoVar
cv)
  | Just Coercion
co <- TCvSubst -> CoVar -> Maybe Coercion
lookupCoVar (LiftingContext -> TCvSubst
lcTCvSubst LiftingContext
env) CoVar
cv
  = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> LiftingContext
zapLiftingContext LiftingContext
env) Bool
sym Bool
rep Role
r Coercion
co

  | Type
ty1 Type -> Type -> Bool
`eqType` Type
ty2   -- See Note [Optimise CoVarCo to Refl]
  = Role -> Type -> Coercion
mkReflCo (Bool -> Role -> Role
chooseRole Bool
rep Role
r) Type
ty1

  | Bool
otherwise
  = forall a. HasCallStack => Bool -> a -> a
assert (CoVar -> Bool
isCoVar CoVar
cv1 )
    Bool -> Role -> Coercion -> Coercion
wrapRole Bool
rep Role
r forall a b. (a -> b) -> a -> b
$ Bool -> Coercion -> Coercion
wrapSym Bool
sym forall a b. (a -> b) -> a -> b
$
    CoVar -> Coercion
CoVarCo CoVar
cv1

  where
    Pair Type
ty1 Type
ty2 = HasDebugCallStack => CoVar -> Pair Type
coVarTypes CoVar
cv1

    cv1 :: CoVar
cv1 = case InScopeSet -> CoVar -> Maybe CoVar
lookupInScope (LiftingContext -> InScopeSet
lcInScopeSet LiftingContext
env) CoVar
cv of
             Just CoVar
cv1 -> CoVar
cv1
             Maybe CoVar
Nothing  -> forall a. HasCallStack => Bool -> String -> SDoc -> a -> a
warnPprTrace Bool
True
                          String
"opt_co: not in scope"
                          (forall a. Outputable a => a -> SDoc
ppr CoVar
cv SDoc -> SDoc -> SDoc
$$ forall a. Outputable a => a -> SDoc
ppr LiftingContext
env)
                          CoVar
cv
          -- cv1 might have a substituted kind!

opt_co4 LiftingContext
_ Bool
_ Bool
_ Role
_ (HoleCo CoercionHole
h)
  = forall a. HasCallStack => String -> SDoc -> a
pprPanic String
"opt_univ fell into a hole" (forall a. Outputable a => a -> SDoc
ppr CoercionHole
h)

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (AxiomInstCo CoAxiom Branched
con Int
ind [Coercion]
cos)
    -- Do *not* push sym inside top-level axioms
    -- e.g. if g is a top-level axiom
    --   g a : f a ~ a
    -- then (sym (g ty)) /= g (sym ty) !!
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== forall (br :: BranchFlag). CoAxiom br -> Role
coAxiomRole CoAxiom Branched
con )
    Bool -> Role -> Coercion -> Coercion
wrapRole Bool
rep (forall (br :: BranchFlag). CoAxiom br -> Role
coAxiomRole CoAxiom Branched
con) forall a b. (a -> b) -> a -> b
$
    Bool -> Coercion -> Coercion
wrapSym Bool
sym forall a b. (a -> b) -> a -> b
$
                       -- some sub-cos might be P: use opt_co2
                       -- See Note [Optimising coercion optimisation]
    CoAxiom Branched -> Int -> [Coercion] -> Coercion
AxiomInstCo CoAxiom Branched
con Int
ind (forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (LiftingContext -> Bool -> Role -> Coercion -> Coercion
opt_co2 LiftingContext
env Bool
False)
                                 (CoAxBranch -> [Role]
coAxBranchRoles (forall (br :: BranchFlag). CoAxiom br -> Int -> CoAxBranch
coAxiomNthBranch CoAxiom Branched
con Int
ind))
                                 [Coercion]
cos)
      -- Note that the_co does *not* have sym pushed into it

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (UnivCo UnivCoProvenance
prov Role
_r Type
t1 Type
t2)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r )
    LiftingContext
-> Bool -> UnivCoProvenance -> Role -> Type -> Type -> Coercion
opt_univ LiftingContext
env Bool
sym UnivCoProvenance
prov (Bool -> Role -> Role
chooseRole Bool
rep Role
r) Type
t1 Type
t2

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (TransCo Coercion
co1 Coercion
co2)
                      -- sym (g `o` h) = sym h `o` sym g
  | Bool
sym       = InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
in_scope Coercion
co2' Coercion
co1'
  | Bool
otherwise = InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
in_scope Coercion
co1' Coercion
co2'
  where
    co1' :: Coercion
co1' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r Coercion
co1
    co2' :: Coercion
co2' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r Coercion
co2
    in_scope :: InScopeSet
in_scope = LiftingContext -> InScopeSet
lcInScopeSet LiftingContext
env

opt_co4 LiftingContext
env Bool
_sym Bool
rep Role
r (NthCo Role
_r Int
n Coercion
co)
  | Just (Type
ty, Role
_) <- Coercion -> Maybe (Type, Role)
isReflCo_maybe Coercion
co
  , Just (TyCon
_tc, [Type]
args) <- forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r )
                        HasDebugCallStack => Type -> Maybe (TyCon, [Type])
splitTyConApp_maybe Type
ty
  = HasDebugCallStack => Role -> LiftingContext -> Type -> Coercion
liftCoSubst (Bool -> Role -> Role
chooseRole Bool
rep Role
r) LiftingContext
env ([Type]
args forall a. Outputable a => [a] -> Int -> a
`getNth` Int
n)

  | Just (Type
ty, Role
_) <- Coercion -> Maybe (Type, Role)
isReflCo_maybe Coercion
co
  , Int
n forall a. Eq a => a -> a -> Bool
== Int
0
  , Just (CoVar
tv, Type
_) <- Type -> Maybe (CoVar, Type)
splitForAllTyCoVar_maybe Type
ty
      -- works for both tyvar and covar
  = HasDebugCallStack => Role -> LiftingContext -> Type -> Coercion
liftCoSubst (Bool -> Role -> Role
chooseRole Bool
rep Role
r) LiftingContext
env (CoVar -> Type
varType CoVar
tv)

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (NthCo Role
r1 Int
n (TyConAppCo Role
_ TyCon
_ [Coercion]
cos))
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
r1 )
    LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r ([Coercion]
cos forall a. Outputable a => [a] -> Int -> a
`getNth` Int
n)

-- see the definition of GHC.Builtin.Types.Prim.funTyCon
opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (NthCo Role
r1 Int
n (FunCo Role
_r2 Coercion
w Coercion
co1 Coercion
co2))
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
r1 )
    LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r (Int -> Coercion -> Coercion -> Coercion -> Coercion
mkNthCoFunCo Int
n Coercion
w Coercion
co1 Coercion
co2)

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (NthCo Role
_r Int
n (ForAllCo CoVar
_ Coercion
eta Coercion
_))
      -- works for both tyvar and covar
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
_r )
    forall a. HasCallStack => Bool -> a -> a
assert (Int
n forall a. Eq a => a -> a -> Bool
== Int
0 )
    LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
Nominal Coercion
eta

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (NthCo Role
_r Int
n Coercion
co)
  | Just Coercion
nth_co <- case Coercion
co' of
      TyConAppCo Role
_ TyCon
_ [Coercion]
cos -> forall a. a -> Maybe a
Just ([Coercion]
cos forall a. Outputable a => [a] -> Int -> a
`getNth` Int
n)
      FunCo Role
_ Coercion
w Coercion
co1 Coercion
co2  -> forall a. a -> Maybe a
Just (Int -> Coercion -> Coercion -> Coercion -> Coercion
mkNthCoFunCo Int
n Coercion
w Coercion
co1 Coercion
co2)
      ForAllCo CoVar
_ Coercion
eta Coercion
_   -> forall a. a -> Maybe a
Just Coercion
eta
      Coercion
_                  -> forall a. Maybe a
Nothing
  = if Bool
rep Bool -> Bool -> Bool
&& (Role
r forall a. Eq a => a -> a -> Bool
== Role
Nominal)
      -- keep propagating the SubCo
    then LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> LiftingContext
zapLiftingContext LiftingContext
env) Bool
False Bool
True Role
Nominal Coercion
nth_co
    else Coercion
nth_co

  | Bool
otherwise
  = Bool -> Role -> Coercion -> Coercion
wrapRole Bool
rep Role
r forall a b. (a -> b) -> a -> b
$ Role -> Int -> Coercion -> Coercion
NthCo Role
r Int
n Coercion
co'
  where
    co' :: Coercion
co' = LiftingContext -> Bool -> Coercion -> Coercion
opt_co1 LiftingContext
env Bool
sym Coercion
co

opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (LRCo LeftOrRight
lr Coercion
co)
  | Just (Coercion, Coercion)
pr_co <- Coercion -> Maybe (Coercion, Coercion)
splitAppCo_maybe Coercion
co
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
Nominal )
    LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
Nominal (forall {a}. LeftOrRight -> (a, a) -> a
pick_lr LeftOrRight
lr (Coercion, Coercion)
pr_co)
  | Just (Coercion, Coercion)
pr_co <- Coercion -> Maybe (Coercion, Coercion)
splitAppCo_maybe Coercion
co'
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
Nominal) forall a b. (a -> b) -> a -> b
$
    if Bool
rep
    then LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> LiftingContext
zapLiftingContext LiftingContext
env) Bool
False Bool
True Role
Nominal (forall {a}. LeftOrRight -> (a, a) -> a
pick_lr LeftOrRight
lr (Coercion, Coercion)
pr_co)
    else forall {a}. LeftOrRight -> (a, a) -> a
pick_lr LeftOrRight
lr (Coercion, Coercion)
pr_co
  | Bool
otherwise
  = Bool -> Role -> Coercion -> Coercion
wrapRole Bool
rep Role
Nominal forall a b. (a -> b) -> a -> b
$ LeftOrRight -> Coercion -> Coercion
LRCo LeftOrRight
lr Coercion
co'
  where
    co' :: Coercion
co' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
co

    pick_lr :: LeftOrRight -> (a, a) -> a
pick_lr LeftOrRight
CLeft  (a
l, a
_) = a
l
    pick_lr LeftOrRight
CRight (a
_, a
r) = a
r

-- See Note [Optimising InstCo]
opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (InstCo Coercion
co1 Coercion
arg)
    -- forall over type...
  | Just (CoVar
tv, Coercion
kind_co, Coercion
co_body) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_ty_maybe Coercion
co1
  = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> CoVar -> Coercion -> LiftingContext
extendLiftingContext LiftingContext
env CoVar
tv
                    (Role -> Type -> Coercion -> Coercion -> Coercion
mkCoherenceRightCo Role
Nominal Type
t2 (Coercion -> Coercion
mkSymCo Coercion
kind_co) Coercion
sym_arg))
                   -- mkSymCo kind_co :: k1 ~ k2
                   -- sym_arg :: (t1 :: k1) ~ (t2 :: k2)
                   -- tv |-> (t1 :: k1) ~ (((t2 :: k2) |> (sym kind_co)) :: k1)
                 Bool
sym Bool
rep Role
r Coercion
co_body

    -- forall over coercion...
  | Just (CoVar
cv, Coercion
kind_co, Coercion
co_body) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_co_maybe Coercion
co1
  , CoercionTy Coercion
h1 <- Type
t1
  , CoercionTy Coercion
h2 <- Type
t2
  = let new_co :: Coercion
new_co = CoVar -> Coercion -> Coercion -> Coercion -> Coercion
mk_new_co CoVar
cv (LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
kind_co) Coercion
h1 Coercion
h2
    in LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> CoVar -> Coercion -> LiftingContext
extendLiftingContext LiftingContext
env CoVar
cv Coercion
new_co) Bool
sym Bool
rep Role
r Coercion
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 (CoVar
tv', Coercion
kind_co', Coercion
co_body') <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_ty_maybe Coercion
co1'
  = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> CoVar -> Coercion -> LiftingContext
extendLiftingContext (LiftingContext -> LiftingContext
zapLiftingContext LiftingContext
env) CoVar
tv'
                    (Role -> Type -> Coercion -> Coercion -> Coercion
mkCoherenceRightCo Role
Nominal Type
t2' (Coercion -> Coercion
mkSymCo Coercion
kind_co') Coercion
arg'))
            Bool
False Bool
False Role
r' Coercion
co_body'

    -- forall over coercion...
  | Just (CoVar
cv', Coercion
kind_co', Coercion
co_body') <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_co_maybe Coercion
co1'
  , CoercionTy Coercion
h1' <- Type
t1'
  , CoercionTy Coercion
h2' <- Type
t2'
  = let new_co :: Coercion
new_co = CoVar -> Coercion -> Coercion -> Coercion -> Coercion
mk_new_co CoVar
cv' Coercion
kind_co' Coercion
h1' Coercion
h2'
    in LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap (LiftingContext -> CoVar -> Coercion -> LiftingContext
extendLiftingContext (LiftingContext -> LiftingContext
zapLiftingContext LiftingContext
env) CoVar
cv' Coercion
new_co)
                    Bool
False Bool
False Role
r' Coercion
co_body'

  | Bool
otherwise = Coercion -> Coercion -> Coercion
InstCo Coercion
co1' Coercion
arg'
  where
    co1' :: Coercion
co1'    = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
rep Role
r Coercion
co1
    r' :: Role
r'      = Bool -> Role -> Role
chooseRole Bool
rep Role
r
    arg' :: Coercion
arg'    = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
arg
    sym_arg :: Coercion
sym_arg = Bool -> Coercion -> Coercion
wrapSym Bool
sym Coercion
arg'

    -- Performance note: don't be alarmed by the two calls to coercionKind
    -- here, as only one call to coercionKind is actually demanded per guard.
    -- t1/t2 are used when checking if co1 is a forall, and t1'/t2' are used
    -- when checking if co1' (i.e., co1 post-optimization) is a forall.
    --
    -- t1/t2 must come from sym_arg, not arg', since it's possible that arg'
    -- might have an extra Sym at the front (after being optimized) that co1
    -- lacks, so we need to use sym_arg to balance the number of Syms. (#15725)
    Pair Type
t1  Type
t2  = Coercion -> Pair Type
coercionKind Coercion
sym_arg
    Pair Type
t1' Type
t2' = Coercion -> Pair Type
coercionKind Coercion
arg'

    mk_new_co :: CoVar -> Coercion -> Coercion -> Coercion -> Coercion
mk_new_co CoVar
cv Coercion
kind_co Coercion
h1 Coercion
h2
      = let -- h1 :: (t1 ~ t2)
            -- h2 :: (t3 ~ t4)
            -- kind_co :: (t1 ~ t2) ~ (t3 ~ t4)
            -- n1 :: t1 ~ t3
            -- n2 :: t2 ~ t4
            -- new_co = (h1 :: t1 ~ t2) ~ ((n1;h2;sym n2) :: t1 ~ t2)
            r2 :: Role
r2  = CoVar -> Role
coVarRole CoVar
cv
            kind_co' :: Coercion
kind_co' = Role -> Role -> Coercion -> Coercion
downgradeRole Role
r2 Role
Nominal Coercion
kind_co
            n1 :: Coercion
n1 = HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r2 Int
2 Coercion
kind_co'
            n2 :: Coercion
n2 = HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r2 Int
3 Coercion
kind_co'
         in Role -> Coercion -> Coercion -> Coercion -> Coercion
mkProofIrrelCo Role
Nominal (Type -> Coercion
Refl (Coercion -> Type
coercionType Coercion
h1)) Coercion
h1
                           (Coercion
n1 Coercion -> Coercion -> Coercion
`mkTransCo` Coercion
h2 Coercion -> Coercion -> Coercion
`mkTransCo` (Coercion -> Coercion
mkSymCo Coercion
n2))

opt_co4 LiftingContext
env Bool
sym Bool
_rep Role
r (KindCo Coercion
co)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
Nominal) forall a b. (a -> b) -> a -> b
$
    let kco' :: Coercion
kco' = Coercion -> Coercion
promoteCoercion Coercion
co in
    case Coercion
kco' of
      KindCo Coercion
co' -> Coercion -> Coercion
promoteCoercion (LiftingContext -> Bool -> Coercion -> Coercion
opt_co1 LiftingContext
env Bool
sym Coercion
co')
      Coercion
_          -> LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
kco'
  -- This might be able to be optimized more to do the promotion
  -- and substitution/optimization at the same time

opt_co4 LiftingContext
env Bool
sym Bool
_ Role
r (SubCo Coercion
co)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== Role
Representational) forall a b. (a -> b) -> a -> b
$
    LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
True Role
Nominal Coercion
co

-- This could perhaps be optimized more.
opt_co4 LiftingContext
env Bool
sym Bool
rep Role
r (AxiomRuleCo CoAxiomRule
co [Coercion]
cs)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r forall a. Eq a => a -> a -> Bool
== CoAxiomRule -> Role
coaxrRole CoAxiomRule
co) forall a b. (a -> b) -> a -> b
$
    Bool -> Role -> Coercion -> Coercion
wrapRole Bool
rep Role
r forall a b. (a -> b) -> a -> b
$
    Bool -> Coercion -> Coercion
wrapSym Bool
sym forall a b. (a -> b) -> a -> b
$
    CoAxiomRule -> [Coercion] -> Coercion
AxiomRuleCo CoAxiomRule
co (forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (LiftingContext -> Bool -> Role -> Coercion -> Coercion
opt_co2 LiftingContext
env Bool
False) (CoAxiomRule -> [Role]
coaxrAsmpRoles CoAxiomRule
co) [Coercion]
cs)

{- Note [Optimise CoVarCo to Refl]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If we have (c :: t~t) we can optimise it to Refl. That increases the
chances of floating the Refl upwards; e.g. Maybe c --> Refl (Maybe t)

We do so here in optCoercion, not in mkCoVarCo; see Note [mkCoVarCo]
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 :: LiftingContext -> Bool -> Coercion -> Coercion
opt_phantom LiftingContext
env Bool
sym Coercion
co
  = LiftingContext
-> Bool -> UnivCoProvenance -> Role -> Type -> Type -> Coercion
opt_univ LiftingContext
env Bool
sym (Coercion -> UnivCoProvenance
PhantomProv (Coercion -> Coercion
mkKindCo Coercion
co)) Role
Phantom Type
ty1 Type
ty2
  where
    Pair Type
ty1 Type
ty2 = Coercion -> Pair Type
coercionKind Coercion
co

{- Note [Differing kinds]
   ~~~~~~~~~~~~~~~~~~~~~~
The two types may not have the same kind (although that would be very unusual).
But even if they have the same kind, and the same type constructor, the number
of arguments in a `CoTyConApp` can differ. Consider

  Any :: forall k. k

  Any * Int                      :: *
  Any (*->*) Maybe Int  :: *

Hence the need to compare argument lengths; see #13658

Note [opt_univ needs injectivity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If opt_univ sees a coercion between `T a1 a2` and `T b1 b2` it will optimize it
by producing a TyConAppCo for T, and pushing the UnivCo into the arguments.  But
this works only if T is injective. Otherwise we can have something like

  type family F x where
    F Int  = Int
    F Bool = Int

where `UnivCo :: F Int ~ F Bool` is reasonable (it is effectively just an
alternative representation for a couple of uses of AxiomInstCos) but we do not
want to produce `F (UnivCo :: Int ~ Bool)` where the inner coercion is clearly
inconsistent.  Hence the opt_univ case for TyConApps checks isInjectiveTyCon.
See #19509.

 -}

opt_univ :: LiftingContext -> SymFlag -> UnivCoProvenance -> Role
         -> Type -> Type -> Coercion
opt_univ :: LiftingContext
-> Bool -> UnivCoProvenance -> Role -> Type -> Type -> Coercion
opt_univ LiftingContext
env Bool
sym (PhantomProv Coercion
h) Role
_r Type
ty1 Type
ty2
  | Bool
sym       = Coercion -> Type -> Type -> Coercion
mkPhantomCo Coercion
h' Type
ty2' Type
ty1'
  | Bool
otherwise = Coercion -> Type -> Type -> Coercion
mkPhantomCo Coercion
h' Type
ty1' Type
ty2'
  where
    h' :: Coercion
h' = LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4 LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
h
    ty1' :: Type
ty1' = HasDebugCallStack => TCvSubst -> Type -> Type
substTy (LiftingContext -> TCvSubst
lcSubstLeft  LiftingContext
env) Type
ty1
    ty2' :: Type
ty2' = HasDebugCallStack => TCvSubst -> Type -> Type
substTy (LiftingContext -> TCvSubst
lcSubstRight LiftingContext
env) Type
ty2

opt_univ LiftingContext
env Bool
sym UnivCoProvenance
prov Role
role Type
oty1 Type
oty2
  | Just (TyCon
tc1, [Type]
tys1) <- HasDebugCallStack => Type -> Maybe (TyCon, [Type])
splitTyConApp_maybe Type
oty1
  , Just (TyCon
tc2, [Type]
tys2) <- HasDebugCallStack => Type -> Maybe (TyCon, [Type])
splitTyConApp_maybe Type
oty2
  , TyCon
tc1 forall a. Eq a => a -> a -> Bool
== TyCon
tc2
  , TyCon -> Role -> Bool
isInjectiveTyCon TyCon
tc1 Role
role  -- see Note [opt_univ needs injectivity]
  , forall a b. [a] -> [b] -> Bool
equalLength [Type]
tys1 [Type]
tys2 -- see Note [Differing kinds]
      -- 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 :: [Role]
roles    = Role -> TyCon -> [Role]
tyConRolesX Role
role TyCon
tc1
        arg_cos :: [Coercion]
arg_cos  = forall a b c d. (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith3 (UnivCoProvenance -> Role -> Type -> Type -> Coercion
mkUnivCo UnivCoProvenance
prov') [Role]
roles [Type]
tys1 [Type]
tys2
        arg_cos' :: [Coercion]
arg_cos' = forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4 LiftingContext
env Bool
sym Bool
False) [Role]
roles [Coercion]
arg_cos
    in
    HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
role TyCon
tc1 [Coercion]
arg_cos'

  -- can't optimize the AppTy case because we can't build the kind coercions.

  | Just (CoVar
tv1, Type
ty1) <- Type -> Maybe (CoVar, Type)
splitForAllTyVar_maybe Type
oty1
  , Just (CoVar
tv2, Type
ty2) <- Type -> Maybe (CoVar, Type)
splitForAllTyVar_maybe Type
oty2
      -- NB: prov isn't interesting here either
  = let k1 :: Type
k1   = CoVar -> Type
tyVarKind CoVar
tv1
        k2 :: Type
k2   = CoVar -> Type
tyVarKind CoVar
tv2
        eta :: Coercion
eta  = UnivCoProvenance -> Role -> Type -> Type -> Coercion
mkUnivCo UnivCoProvenance
prov' Role
Nominal Type
k1 Type
k2
          -- eta gets opt'ed soon, but not yet.
        ty2' :: Type
ty2' = HasDebugCallStack => [CoVar] -> [Type] -> Type -> Type
substTyWith [CoVar
tv2] [CoVar -> Type
TyVarTy CoVar
tv1 Type -> Coercion -> Type
`mkCastTy` Coercion
eta] Type
ty2

        (LiftingContext
env', CoVar
tv1', Coercion
eta') = LiftingContext
-> Bool -> CoVar -> Coercion -> (LiftingContext, CoVar, Coercion)
optForAllCoBndr LiftingContext
env Bool
sym CoVar
tv1 Coercion
eta
    in
    CoVar -> Coercion -> Coercion -> Coercion
mkForAllCo CoVar
tv1' Coercion
eta' (LiftingContext
-> Bool -> UnivCoProvenance -> Role -> Type -> Type -> Coercion
opt_univ LiftingContext
env' Bool
sym UnivCoProvenance
prov' Role
role Type
ty1 Type
ty2')

  | Just (CoVar
cv1, Type
ty1) <- Type -> Maybe (CoVar, Type)
splitForAllCoVar_maybe Type
oty1
  , Just (CoVar
cv2, Type
ty2) <- Type -> Maybe (CoVar, Type)
splitForAllCoVar_maybe Type
oty2
      -- NB: prov isn't interesting here either
  = let k1 :: Type
k1    = CoVar -> Type
varType CoVar
cv1
        k2 :: Type
k2    = CoVar -> Type
varType CoVar
cv2
        r' :: Role
r'    = CoVar -> Role
coVarRole CoVar
cv1
        eta :: Coercion
eta   = UnivCoProvenance -> Role -> Type -> Type -> Coercion
mkUnivCo UnivCoProvenance
prov' Role
Nominal Type
k1 Type
k2
        eta_d :: Coercion
eta_d = Role -> Role -> Coercion -> Coercion
downgradeRole Role
r' Role
Nominal Coercion
eta
          -- eta gets opt'ed soon, but not yet.
        n_co :: Coercion
n_co  = (Coercion -> Coercion
mkSymCo forall a b. (a -> b) -> a -> b
$ HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r' Int
2 Coercion
eta_d) Coercion -> Coercion -> Coercion
`mkTransCo`
                (CoVar -> Coercion
mkCoVarCo CoVar
cv1) Coercion -> Coercion -> Coercion
`mkTransCo`
                (HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r' Int
3 Coercion
eta_d)
        ty2' :: Type
ty2'  = [CoVar] -> [Coercion] -> Type -> Type
substTyWithCoVars [CoVar
cv2] [Coercion
n_co] Type
ty2

        (LiftingContext
env', CoVar
cv1', Coercion
eta') = LiftingContext
-> Bool -> CoVar -> Coercion -> (LiftingContext, CoVar, Coercion)
optForAllCoBndr LiftingContext
env Bool
sym CoVar
cv1 Coercion
eta
    in
    CoVar -> Coercion -> Coercion -> Coercion
mkForAllCo CoVar
cv1' Coercion
eta' (LiftingContext
-> Bool -> UnivCoProvenance -> Role -> Type -> Type -> Coercion
opt_univ LiftingContext
env' Bool
sym UnivCoProvenance
prov' Role
role Type
ty1 Type
ty2')

  | Bool
otherwise
  = let ty1 :: Type
ty1 = TCvSubst -> Type -> Type
substTyUnchecked (LiftingContext -> TCvSubst
lcSubstLeft  LiftingContext
env) Type
oty1
        ty2 :: Type
ty2 = TCvSubst -> Type -> Type
substTyUnchecked (LiftingContext -> TCvSubst
lcSubstRight LiftingContext
env) Type
oty2
        (Type
a, Type
b) | Bool
sym       = (Type
ty2, Type
ty1)
               | Bool
otherwise = (Type
ty1, Type
ty2)
    in
    UnivCoProvenance -> Role -> Type -> Type -> Coercion
mkUnivCo UnivCoProvenance
prov' Role
role Type
a Type
b

  where
    prov' :: UnivCoProvenance
prov' = case UnivCoProvenance
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
#endif
      ProofIrrelProv Coercion
kco -> Coercion -> UnivCoProvenance
ProofIrrelProv forall a b. (a -> b) -> a -> b
$ LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal Coercion
kco
      PluginProv String
_       -> UnivCoProvenance
prov
      CorePrepProv Bool
_     -> UnivCoProvenance
prov

-------------
opt_transList :: HasDebugCallStack => InScopeSet -> [NormalCo] -> [NormalCo] -> [NormalCo]
opt_transList :: HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is = forall a b c. String -> (a -> b -> c) -> [a] -> [b] -> [c]
zipWithEqual String
"opt_transList" (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is)
  -- The input lists must have identical length.

opt_trans :: InScopeSet -> NormalCo -> NormalCo -> NormalCo
opt_trans :: InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1 Coercion
co2
  | Coercion -> Bool
isReflCo Coercion
co1 = Coercion
co2
    -- optimize when co1 is a Refl Co
  | Bool
otherwise    = InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans1 InScopeSet
is Coercion
co1 Coercion
co2

opt_trans1 :: InScopeSet -> NormalNonIdCo -> NormalCo -> NormalCo
-- First arg is not the identity
opt_trans1 :: InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans1 InScopeSet
is Coercion
co1 Coercion
co2
  | Coercion -> Bool
isReflCo Coercion
co2 = Coercion
co1
    -- optimize when co2 is a Refl Co
  | Bool
otherwise    = InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans2 InScopeSet
is Coercion
co1 Coercion
co2

opt_trans2 :: InScopeSet -> NormalNonIdCo -> NormalNonIdCo -> NormalCo
-- Neither arg is the identity
opt_trans2 :: InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans2 InScopeSet
is (TransCo Coercion
co1a Coercion
co1b) Coercion
co2
    -- Don't know whether the sub-coercions are the identity
  = InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1a (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1b Coercion
co2)

opt_trans2 InScopeSet
is Coercion
co1 Coercion
co2
  | Just Coercion
co <- InScopeSet -> Coercion -> Coercion -> Maybe Coercion
opt_trans_rule InScopeSet
is Coercion
co1 Coercion
co2
  = Coercion
co

opt_trans2 InScopeSet
is Coercion
co1 (TransCo Coercion
co2a Coercion
co2b)
  | Just Coercion
co1_2a <- InScopeSet -> Coercion -> Coercion -> Maybe Coercion
opt_trans_rule InScopeSet
is Coercion
co1 Coercion
co2a
  = if Coercion -> Bool
isReflCo Coercion
co1_2a
    then Coercion
co2b
    else InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans1 InScopeSet
is Coercion
co1_2a Coercion
co2b

opt_trans2 InScopeSet
_ Coercion
co1 Coercion
co2
  = Coercion -> Coercion -> Coercion
mkTransCo Coercion
co1 Coercion
co2

------
-- Optimize coercions with a top-level use of transitivity.
opt_trans_rule :: InScopeSet -> NormalNonIdCo -> NormalNonIdCo -> Maybe NormalCo

opt_trans_rule :: InScopeSet -> Coercion -> Coercion -> Maybe Coercion
opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(GRefl Role
r1 Type
t1 (MCo Coercion
co1)) in_co2 :: Coercion
in_co2@(GRefl Role
r2 Type
_ (MCo Coercion
co2))
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r1 forall a. Eq a => a -> a -> Bool
== Role
r2) forall a b. (a -> b) -> a -> b
$
    String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"GRefl" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    Role -> Type -> Coercion -> Coercion
mkGReflRightCo Role
r1 Type
t1 (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1 Coercion
co2)

-- Push transitivity through matching destructors
opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(NthCo Role
r1 Int
d1 Coercion
co1) in_co2 :: Coercion
in_co2@(NthCo Role
r2 Int
d2 Coercion
co2)
  | Int
d1 forall a. Eq a => a -> a -> Bool
== Int
d2
  , Coercion -> Role
coercionRole Coercion
co1 forall a. Eq a => a -> a -> Bool
== Coercion -> Role
coercionRole Coercion
co2
  , Coercion
co1 Coercion -> Coercion -> Bool
`compatible_co` Coercion
co2
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r1 forall a. Eq a => a -> a -> Bool
== Role
r2) forall a b. (a -> b) -> a -> b
$
    String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"PushNth" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r1 Int
d1 (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1 Coercion
co2)

opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(LRCo LeftOrRight
d1 Coercion
co1) in_co2 :: Coercion
in_co2@(LRCo LeftOrRight
d2 Coercion
co2)
  | LeftOrRight
d1 forall a. Eq a => a -> a -> Bool
== LeftOrRight
d2
  , Coercion
co1 Coercion -> Coercion -> Bool
`compatible_co` Coercion
co2
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"PushLR" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    LeftOrRight -> Coercion -> Coercion
mkLRCo LeftOrRight
d1 (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1 Coercion
co2)

-- Push transitivity inside instantiation
opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(InstCo Coercion
co1 Coercion
ty1) in_co2 :: Coercion
in_co2@(InstCo Coercion
co2 Coercion
ty2)
  | Coercion
ty1 Coercion -> Coercion -> Bool
`eqCoercion` Coercion
ty2
  , Coercion
co1 Coercion -> Coercion -> Bool
`compatible_co` Coercion
co2
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"TrPushInst" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    Coercion -> Coercion -> Coercion
mkInstCo (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1 Coercion
co2) Coercion
ty1

opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(UnivCo UnivCoProvenance
p1 Role
r1 Type
tyl1 Type
_tyr1)
                  in_co2 :: Coercion
in_co2@(UnivCo UnivCoProvenance
p2 Role
r2 Type
_tyl2 Type
tyr2)
  | Just UnivCoProvenance
prov' <- UnivCoProvenance -> UnivCoProvenance -> Maybe UnivCoProvenance
opt_trans_prov UnivCoProvenance
p1 UnivCoProvenance
p2
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r1 forall a. Eq a => a -> a -> Bool
== Role
r2) forall a b. (a -> b) -> a -> b
$
    String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"UnivCo" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    UnivCoProvenance -> Role -> Type -> Type -> Coercion
mkUnivCo UnivCoProvenance
prov' Role
r1 Type
tyl1 Type
tyr2
  where
    -- if the provenances are different, opt'ing will be very confusing
    opt_trans_prov :: UnivCoProvenance -> UnivCoProvenance -> Maybe UnivCoProvenance
opt_trans_prov (PhantomProv Coercion
kco1)    (PhantomProv Coercion
kco2)
      = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Coercion -> UnivCoProvenance
PhantomProv forall a b. (a -> b) -> a -> b
$ InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
kco1 Coercion
kco2
    opt_trans_prov (ProofIrrelProv Coercion
kco1) (ProofIrrelProv Coercion
kco2)
      = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Coercion -> UnivCoProvenance
ProofIrrelProv forall a b. (a -> b) -> a -> b
$ InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
kco1 Coercion
kco2
    opt_trans_prov (PluginProv String
str1)     (PluginProv String
str2)     | String
str1 forall a. Eq a => a -> a -> Bool
== String
str2 = forall a. a -> Maybe a
Just UnivCoProvenance
p1
    opt_trans_prov UnivCoProvenance
_ UnivCoProvenance
_ = forall a. Maybe a
Nothing

-- Push transitivity down through matching top-level constructors.
opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(TyConAppCo Role
r1 TyCon
tc1 [Coercion]
cos1) in_co2 :: Coercion
in_co2@(TyConAppCo Role
r2 TyCon
tc2 [Coercion]
cos2)
  | TyCon
tc1 forall a. Eq a => a -> a -> Bool
== TyCon
tc2
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r1 forall a. Eq a => a -> a -> Bool
== Role
r2) forall a b. (a -> b) -> a -> b
$
    String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"PushTyConApp" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
r1 TyCon
tc1 (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos1 [Coercion]
cos2)

opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(FunCo Role
r1 Coercion
w1 Coercion
co1a Coercion
co1b) in_co2 :: Coercion
in_co2@(FunCo Role
r2 Coercion
w2 Coercion
co2a Coercion
co2b)
  = forall a. HasCallStack => Bool -> a -> a
assert (Role
r1 forall a. Eq a => a -> a -> Bool
== Role
r2) forall a b. (a -> b) -> a -> b
$   -- Just like the TyConAppCo/TyConAppCo case
    String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"PushFun" Coercion
in_co1 Coercion
in_co2 forall a b. (a -> b) -> a -> b
$
    Role -> Coercion -> Coercion -> Coercion -> Coercion
mkFunCo Role
r1 (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
w1 Coercion
w2) (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1a Coercion
co2a) (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1b Coercion
co2b)

opt_trans_rule InScopeSet
is in_co1 :: Coercion
in_co1@(AppCo Coercion
co1a Coercion
co1b) in_co2 :: Coercion
in_co2@(AppCo Coercion
co2a Coercion
co2b)
  -- Must call opt_trans_rule_app; see Note [EtaAppCo]
  = InScopeSet
-> Coercion
-> Coercion
-> Coercion
-> [Coercion]
-> Coercion
-> [Coercion]
-> Maybe Coercion
opt_trans_rule_app InScopeSet
is Coercion
in_co1 Coercion
in_co2 Coercion
co1a [Coercion
co1b] Coercion
co2a [Coercion
co2b]

-- Eta rules
opt_trans_rule InScopeSet
is co1 :: Coercion
co1@(TyConAppCo Role
r TyCon
tc [Coercion]
cos1) Coercion
co2
  | Just [Coercion]
cos2 <- TyCon -> Coercion -> Maybe [Coercion]
etaTyConAppCo_maybe TyCon
tc Coercion
co2
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"EtaCompL" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$
    HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
r TyCon
tc (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos1 [Coercion]
cos2)

opt_trans_rule InScopeSet
is Coercion
co1 co2 :: Coercion
co2@(TyConAppCo Role
r TyCon
tc [Coercion]
cos2)
  | Just [Coercion]
cos1 <- TyCon -> Coercion -> Maybe [Coercion]
etaTyConAppCo_maybe TyCon
tc Coercion
co1
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"EtaCompR" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$
    HasDebugCallStack => Role -> TyCon -> [Coercion] -> Coercion
mkTyConAppCo Role
r TyCon
tc (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos1 [Coercion]
cos2)

opt_trans_rule InScopeSet
is co1 :: Coercion
co1@(AppCo Coercion
co1a Coercion
co1b) Coercion
co2
  | Just (Coercion
co2a,Coercion
co2b) <- Coercion -> Maybe (Coercion, Coercion)
etaAppCo_maybe Coercion
co2
  = InScopeSet
-> Coercion
-> Coercion
-> Coercion
-> [Coercion]
-> Coercion
-> [Coercion]
-> Maybe Coercion
opt_trans_rule_app InScopeSet
is Coercion
co1 Coercion
co2 Coercion
co1a [Coercion
co1b] Coercion
co2a [Coercion
co2b]

opt_trans_rule InScopeSet
is Coercion
co1 co2 :: Coercion
co2@(AppCo Coercion
co2a Coercion
co2b)
  | Just (Coercion
co1a,Coercion
co1b) <- Coercion -> Maybe (Coercion, Coercion)
etaAppCo_maybe Coercion
co1
  = InScopeSet
-> Coercion
-> Coercion
-> Coercion
-> [Coercion]
-> Coercion
-> [Coercion]
-> Maybe Coercion
opt_trans_rule_app InScopeSet
is Coercion
co1 Coercion
co2 Coercion
co1a [Coercion
co1b] Coercion
co2a [Coercion
co2b]

-- Push transitivity inside forall
-- forall over types.
opt_trans_rule InScopeSet
is Coercion
co1 Coercion
co2
  | Just (CoVar
tv1, Coercion
eta1, Coercion
r1) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_ty_maybe Coercion
co1
  , Just (CoVar
tv2, Coercion
eta2, Coercion
r2) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
etaForAllCo_ty_maybe Coercion
co2
  = CoVar
-> Coercion
-> Coercion
-> CoVar
-> Coercion
-> Coercion
-> Maybe Coercion
push_trans CoVar
tv1 Coercion
eta1 Coercion
r1 CoVar
tv2 Coercion
eta2 Coercion
r2

  | Just (CoVar
tv2, Coercion
eta2, Coercion
r2) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_ty_maybe Coercion
co2
  , Just (CoVar
tv1, Coercion
eta1, Coercion
r1) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
etaForAllCo_ty_maybe Coercion
co1
  = CoVar
-> Coercion
-> Coercion
-> CoVar
-> Coercion
-> Coercion
-> Maybe Coercion
push_trans CoVar
tv1 Coercion
eta1 Coercion
r1 CoVar
tv2 Coercion
eta2 Coercion
r2

  where
  push_trans :: CoVar
-> Coercion
-> Coercion
-> CoVar
-> Coercion
-> Coercion
-> Maybe Coercion
push_trans CoVar
tv1 Coercion
eta1 Coercion
r1 CoVar
tv2 Coercion
eta2 Coercion
r2
    -- Given:
    --   co1 = /\ tv1 : eta1. r1
    --   co2 = /\ tv2 : eta2. r2
    -- Wanted:
    --   /\tv1 : (eta1;eta2).  (r1; r2[tv2 |-> tv1 |> eta1])
    = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"EtaAllTy_ty" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$
      CoVar -> Coercion -> Coercion -> Coercion
mkForAllCo CoVar
tv1 (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
eta1 Coercion
eta2) (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is' Coercion
r1 Coercion
r2')
    where
      is' :: InScopeSet
is' = InScopeSet
is InScopeSet -> CoVar -> InScopeSet
`extendInScopeSet` CoVar
tv1
      r2' :: Coercion
r2' = [CoVar] -> [Type] -> Coercion -> Coercion
substCoWithUnchecked [CoVar
tv2] [Type -> Coercion -> Type
mkCastTy (CoVar -> Type
TyVarTy CoVar
tv1) Coercion
eta1] Coercion
r2

-- Push transitivity inside forall
-- forall over coercions.
opt_trans_rule InScopeSet
is Coercion
co1 Coercion
co2
  | Just (CoVar
cv1, Coercion
eta1, Coercion
r1) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_co_maybe Coercion
co1
  , Just (CoVar
cv2, Coercion
eta2, Coercion
r2) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
etaForAllCo_co_maybe Coercion
co2
  = CoVar
-> Coercion
-> Coercion
-> CoVar
-> Coercion
-> Coercion
-> Maybe Coercion
push_trans CoVar
cv1 Coercion
eta1 Coercion
r1 CoVar
cv2 Coercion
eta2 Coercion
r2

  | Just (CoVar
cv2, Coercion
eta2, Coercion
r2) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_co_maybe Coercion
co2
  , Just (CoVar
cv1, Coercion
eta1, Coercion
r1) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
etaForAllCo_co_maybe Coercion
co1
  = CoVar
-> Coercion
-> Coercion
-> CoVar
-> Coercion
-> Coercion
-> Maybe Coercion
push_trans CoVar
cv1 Coercion
eta1 Coercion
r1 CoVar
cv2 Coercion
eta2 Coercion
r2

  where
  push_trans :: CoVar
-> Coercion
-> Coercion
-> CoVar
-> Coercion
-> Coercion
-> Maybe Coercion
push_trans CoVar
cv1 Coercion
eta1 Coercion
r1 CoVar
cv2 Coercion
eta2 Coercion
r2
    -- Given:
    --   co1 = /\ cv1 : eta1. r1
    --   co2 = /\ cv2 : eta2. r2
    -- Wanted:
    --   n1 = nth 2 eta1
    --   n2 = nth 3 eta1
    --   nco = /\ cv1 : (eta1;eta2). (r1; r2[cv2 |-> (sym n1);cv1;n2])
    = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"EtaAllTy_co" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$
      CoVar -> Coercion -> Coercion -> Coercion
mkForAllCo CoVar
cv1 (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
eta1 Coercion
eta2) (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is' Coercion
r1 Coercion
r2')
    where
      is' :: InScopeSet
is'  = InScopeSet
is InScopeSet -> CoVar -> InScopeSet
`extendInScopeSet` CoVar
cv1
      role :: Role
role = CoVar -> Role
coVarRole CoVar
cv1
      eta1' :: Coercion
eta1' = Role -> Role -> Coercion -> Coercion
downgradeRole Role
role Role
Nominal Coercion
eta1
      n1 :: Coercion
n1   = HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
role Int
2 Coercion
eta1'
      n2 :: Coercion
n2   = HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
role Int
3 Coercion
eta1'
      r2' :: Coercion
r2'  = HasDebugCallStack => TCvSubst -> Coercion -> Coercion
substCo (HasDebugCallStack => [CoVar] -> [Coercion] -> TCvSubst
zipCvSubst [CoVar
cv2] [(Coercion -> Coercion
mkSymCo Coercion
n1) Coercion -> Coercion -> Coercion
`mkTransCo`
                                        (CoVar -> Coercion
mkCoVarCo CoVar
cv1) Coercion -> Coercion -> Coercion
`mkTransCo` Coercion
n2])
                    Coercion
r2

-- Push transitivity inside axioms
opt_trans_rule InScopeSet
is Coercion
co1 Coercion
co2

  -- See Note [Why call checkAxInstCo during optimisation]
  -- TrPushSymAxR
  | Just (Bool
sym, CoAxiom Branched
con, Int
ind, [Coercion]
cos1) <- Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co1_is_axiom_maybe
  , Bool
True <- Bool
sym
  , Just [Coercion]
cos2 <- forall (br :: BranchFlag).
Bool -> CoAxiom br -> Int -> Coercion -> Maybe [Coercion]
matchAxiom Bool
sym CoAxiom Branched
con Int
ind Coercion
co2
  , let newAxInst :: Coercion
newAxInst = CoAxiom Branched -> Int -> [Coercion] -> Coercion
AxiomInstCo CoAxiom Branched
con Int
ind (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is (forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Coercion
mkSymCo [Coercion]
cos2) [Coercion]
cos1)
  , Maybe CoAxBranch
Nothing <- Coercion -> Maybe CoAxBranch
checkAxInstCo Coercion
newAxInst
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"TrPushSymAxR" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$ Coercion -> Coercion
SymCo Coercion
newAxInst

  -- TrPushAxR
  | Just (Bool
sym, CoAxiom Branched
con, Int
ind, [Coercion]
cos1) <- Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co1_is_axiom_maybe
  , Bool
False <- Bool
sym
  , Just [Coercion]
cos2 <- forall (br :: BranchFlag).
Bool -> CoAxiom br -> Int -> Coercion -> Maybe [Coercion]
matchAxiom Bool
sym CoAxiom Branched
con Int
ind Coercion
co2
  , let newAxInst :: Coercion
newAxInst = CoAxiom Branched -> Int -> [Coercion] -> Coercion
AxiomInstCo CoAxiom Branched
con Int
ind (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos1 [Coercion]
cos2)
  , Maybe CoAxBranch
Nothing <- Coercion -> Maybe CoAxBranch
checkAxInstCo Coercion
newAxInst
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"TrPushAxR" Coercion
co1 Coercion
co2 Coercion
newAxInst

  -- TrPushSymAxL
  | Just (Bool
sym, CoAxiom Branched
con, Int
ind, [Coercion]
cos2) <- Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co2_is_axiom_maybe
  , Bool
True <- Bool
sym
  , Just [Coercion]
cos1 <- forall (br :: BranchFlag).
Bool -> CoAxiom br -> Int -> Coercion -> Maybe [Coercion]
matchAxiom (Bool -> Bool
not Bool
sym) CoAxiom Branched
con Int
ind Coercion
co1
  , let newAxInst :: Coercion
newAxInst = CoAxiom Branched -> Int -> [Coercion] -> Coercion
AxiomInstCo CoAxiom Branched
con Int
ind (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos2 (forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Coercion
mkSymCo [Coercion]
cos1))
  , Maybe CoAxBranch
Nothing <- Coercion -> Maybe CoAxBranch
checkAxInstCo Coercion
newAxInst
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"TrPushSymAxL" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$ Coercion -> Coercion
SymCo Coercion
newAxInst

  -- TrPushAxL
  | Just (Bool
sym, CoAxiom Branched
con, Int
ind, [Coercion]
cos2) <- Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co2_is_axiom_maybe
  , Bool
False <- Bool
sym
  , Just [Coercion]
cos1 <- forall (br :: BranchFlag).
Bool -> CoAxiom br -> Int -> Coercion -> Maybe [Coercion]
matchAxiom (Bool -> Bool
not Bool
sym) CoAxiom Branched
con Int
ind Coercion
co1
  , let newAxInst :: Coercion
newAxInst = CoAxiom Branched -> Int -> [Coercion] -> Coercion
AxiomInstCo CoAxiom Branched
con Int
ind (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos1 [Coercion]
cos2)
  , Maybe CoAxBranch
Nothing <- Coercion -> Maybe CoAxBranch
checkAxInstCo Coercion
newAxInst
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"TrPushAxL" Coercion
co1 Coercion
co2 Coercion
newAxInst

  -- TrPushAxSym/TrPushSymAx
  | Just (Bool
sym1, CoAxiom Branched
con1, Int
ind1, [Coercion]
cos1) <- Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co1_is_axiom_maybe
  , Just (Bool
sym2, CoAxiom Branched
con2, Int
ind2, [Coercion]
cos2) <- Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co2_is_axiom_maybe
  , CoAxiom Branched
con1 forall a. Eq a => a -> a -> Bool
== CoAxiom Branched
con2
  , Int
ind1 forall a. Eq a => a -> a -> Bool
== Int
ind2
  , Bool
sym1 forall a. Eq a => a -> a -> Bool
== Bool -> Bool
not Bool
sym2
  , let branch :: CoAxBranch
branch = forall (br :: BranchFlag). CoAxiom br -> Int -> CoAxBranch
coAxiomNthBranch CoAxiom Branched
con1 Int
ind1
        qtvs :: [CoVar]
qtvs = CoAxBranch -> [CoVar]
coAxBranchTyVars CoAxBranch
branch forall a. [a] -> [a] -> [a]
++ CoAxBranch -> [CoVar]
coAxBranchCoVars CoAxBranch
branch
        lhs :: Type
lhs  = forall (br :: BranchFlag). CoAxiom br -> Int -> Type
coAxNthLHS CoAxiom Branched
con1 Int
ind1
        rhs :: Type
rhs  = CoAxBranch -> Type
coAxBranchRHS CoAxBranch
branch
        pivot_tvs :: TyCoVarSet
pivot_tvs = Type -> TyCoVarSet
exactTyCoVarsOfType (if Bool
sym2 then Type
rhs else Type
lhs)
  , forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (CoVar -> TyCoVarSet -> Bool
`elemVarSet` TyCoVarSet
pivot_tvs) [CoVar]
qtvs
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"TrPushAxSym" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$
    if Bool
sym2
       -- TrPushAxSym
    then Role -> [CoVar] -> [Coercion] -> Type -> Coercion
liftCoSubstWith Role
role [CoVar]
qtvs (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is [Coercion]
cos1 (forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Coercion
mkSymCo [Coercion]
cos2)) Type
lhs
       -- TrPushSymAx
    else Role -> [CoVar] -> [Coercion] -> Type -> Coercion
liftCoSubstWith Role
role [CoVar]
qtvs (HasDebugCallStack =>
InScopeSet -> [Coercion] -> [Coercion] -> [Coercion]
opt_transList InScopeSet
is (forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Coercion
mkSymCo [Coercion]
cos1) [Coercion]
cos2) Type
rhs
  where
    co1_is_axiom_maybe :: Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co1_is_axiom_maybe = Coercion -> Maybe (Bool, CoAxiom Branched, Int, [Coercion])
isAxiom_maybe Coercion
co1
    co2_is_axiom_maybe :: Maybe (Bool, CoAxiom Branched, Int, [Coercion])
co2_is_axiom_maybe = Coercion -> Maybe (Bool, CoAxiom Branched, Int, [Coercion])
isAxiom_maybe Coercion
co2
    role :: Role
role = Coercion -> Role
coercionRole Coercion
co1 -- should be the same as coercionRole co2!

opt_trans_rule InScopeSet
_ Coercion
co1 Coercion
co2        -- Identity rule
  | let ty1 :: Type
ty1 = Coercion -> Type
coercionLKind Coercion
co1
        r :: Role
r   = Coercion -> Role
coercionRole Coercion
co1
        ty2 :: Type
ty2 = Coercion -> Type
coercionRKind Coercion
co2
  , Type
ty1 Type -> Type -> Bool
`eqType` Type
ty2
  = String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
"RedTypeDirRefl" Coercion
co1 Coercion
co2 forall a b. (a -> b) -> a -> b
$
    Role -> Type -> Coercion
mkReflCo Role
r Type
ty2

opt_trans_rule InScopeSet
_ Coercion
_ Coercion
_ = forall a. Maybe a
Nothing

-- See Note [EtaAppCo]
opt_trans_rule_app :: InScopeSet
                   -> Coercion   -- original left-hand coercion (printing only)
                   -> Coercion   -- original right-hand coercion (printing only)
                   -> Coercion   -- left-hand coercion "function"
                   -> [Coercion] -- left-hand coercion "args"
                   -> Coercion   -- right-hand coercion "function"
                   -> [Coercion] -- right-hand coercion "args"
                   -> Maybe Coercion
opt_trans_rule_app :: InScopeSet
-> Coercion
-> Coercion
-> Coercion
-> [Coercion]
-> Coercion
-> [Coercion]
-> Maybe Coercion
opt_trans_rule_app InScopeSet
is Coercion
orig_co1 Coercion
orig_co2 Coercion
co1a [Coercion]
co1bs Coercion
co2a [Coercion]
co2bs
  | AppCo Coercion
co1aa Coercion
co1ab <- Coercion
co1a
  , Just (Coercion
co2aa, Coercion
co2ab) <- Coercion -> Maybe (Coercion, Coercion)
etaAppCo_maybe Coercion
co2a
  = InScopeSet
-> Coercion
-> Coercion
-> Coercion
-> [Coercion]
-> Coercion
-> [Coercion]
-> Maybe Coercion
opt_trans_rule_app InScopeSet
is Coercion
orig_co1 Coercion
orig_co2 Coercion
co1aa (Coercion
co1abforall a. a -> [a] -> [a]
:[Coercion]
co1bs) Coercion
co2aa (Coercion
co2abforall a. a -> [a] -> [a]
:[Coercion]
co2bs)

  | AppCo Coercion
co2aa Coercion
co2ab <- Coercion
co2a
  , Just (Coercion
co1aa, Coercion
co1ab) <- Coercion -> Maybe (Coercion, Coercion)
etaAppCo_maybe Coercion
co1a
  = InScopeSet
-> Coercion
-> Coercion
-> Coercion
-> [Coercion]
-> Coercion
-> [Coercion]
-> Maybe Coercion
opt_trans_rule_app InScopeSet
is Coercion
orig_co1 Coercion
orig_co2 Coercion
co1aa (Coercion
co1abforall a. a -> [a] -> [a]
:[Coercion]
co1bs) Coercion
co2aa (Coercion
co2abforall a. a -> [a] -> [a]
:[Coercion]
co2bs)

  | Bool
otherwise
  = forall a. HasCallStack => Bool -> a -> a
assert ([Coercion]
co1bs forall a b. [a] -> [b] -> Bool
`equalLength` [Coercion]
co2bs) forall a b. (a -> b) -> a -> b
$
    String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule (String
"EtaApps:" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall (t :: * -> *) a. Foldable t => t a -> Int
length [Coercion]
co1bs)) Coercion
orig_co1 Coercion
orig_co2 forall a b. (a -> b) -> a -> b
$
    let rt1a :: Type
rt1a = Coercion -> Type
coercionRKind Coercion
co1a

        lt2a :: Type
lt2a = Coercion -> Type
coercionLKind Coercion
co2a
        rt2a :: Role
rt2a = Coercion -> Role
coercionRole  Coercion
co2a

        rt1bs :: [Type]
rt1bs = forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Type
coercionRKind [Coercion]
co1bs
        lt2bs :: [Type]
lt2bs = forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Type
coercionLKind [Coercion]
co2bs
        rt2bs :: [Role]
rt2bs = forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Role
coercionRole [Coercion]
co2bs

        kcoa :: Coercion
kcoa = Coercion -> Coercion
mkKindCo forall a b. (a -> b) -> a -> b
$ Type -> Type -> Coercion
buildCoercion Type
lt2a Type
rt1a
        kcobs :: [Coercion]
kcobs = forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Coercion
mkKindCo forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Type -> Type -> Coercion
buildCoercion [Type]
lt2bs [Type]
rt1bs

        co2a' :: Coercion
co2a'   = Role -> Type -> Coercion -> Coercion -> Coercion
mkCoherenceLeftCo Role
rt2a Type
lt2a Coercion
kcoa Coercion
co2a
        co2bs' :: [Coercion]
co2bs'  = forall a b c d. (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith3 Role -> Type -> Coercion -> Coercion
mkGReflLeftCo [Role]
rt2bs [Type]
lt2bs [Coercion]
kcobs
        co2bs'' :: [Coercion]
co2bs'' = forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Coercion -> Coercion -> Coercion
mkTransCo [Coercion]
co2bs' [Coercion]
co2bs
    in
    Coercion -> [Coercion] -> Coercion
mkAppCos (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is Coercion
co1a Coercion
co2a')
             (forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (InScopeSet -> Coercion -> Coercion -> Coercion
opt_trans InScopeSet
is) [Coercion]
co1bs [Coercion]
co2bs'')

fireTransRule :: String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule :: String -> Coercion -> Coercion -> Coercion -> Maybe Coercion
fireTransRule String
_rule Coercion
_co1 Coercion
_co2 Coercion
res
  = forall a. a -> Maybe a
Just Coercion
res

{-
Note [Conflict checking with AxiomInstCo]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider the following type family and axiom:

type family Equal (a :: k) (b :: k) :: Bool
type instance where
  Equal a a = True
  Equal a b = False
--
Equal :: forall k::*. k -> k -> Bool
axEqual :: { forall k::*. forall a::k. Equal k a a ~ True
           ; forall k::*. forall a::k. forall b::k. Equal k a b ~ False }

We wish to disallow (axEqual[1] <*> <Int> <Int). (Recall that the index is
0-based, so this is the second branch of the axiom.) The problem is that, on
the surface, it seems that (axEqual[1] <*> <Int> <Int>) :: (Equal * Int Int ~
False) and that all is OK. But, all is not OK: we want to use the first branch
of the axiom in this case, not the second. The problem is that the parameters
of the first branch can unify with the supplied coercions, thus meaning that
the first branch should be taken. See also Note [Apartness] in
"GHC.Core.FamInstEnv".

Note [Why call checkAxInstCo during optimisation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is possible that otherwise-good-looking optimisations meet with disaster
in the presence of axioms with multiple equations. Consider

type family Equal (a :: *) (b :: *) :: Bool where
  Equal a a = True
  Equal a b = False
type family Id (a :: *) :: * where
  Id a = a

axEq :: { [a::*].       Equal a a ~ True
        ; [a::*, b::*]. Equal a b ~ False }
axId :: [a::*]. Id a ~ a

co1 = Equal (axId[0] Int) (axId[0] Bool)
  :: Equal (Id Int) (Id Bool) ~  Equal Int Bool
co2 = axEq[1] <Int> <Bool>
  :: Equal Int Bool ~ False

We wish to optimise (co1 ; co2). We end up in rule TrPushAxL, noting that
co2 is an axiom and that matchAxiom succeeds when looking at co1. But, what
happens when we push the coercions inside? We get

co3 = axEq[1] (axId[0] Int) (axId[0] Bool)
  :: Equal (Id Int) (Id Bool) ~ False

which is bogus! This is because the type system isn't smart enough to know
that (Id Int) and (Id Bool) are Surely Apart, as they're headed by type
families. At the time of writing, I (Richard Eisenberg) couldn't think of
a way of detecting this any more efficient than just building the optimised
coercion and checking.

Note [EtaAppCo]
~~~~~~~~~~~~~~~
Suppose we're trying to optimize (co1a co1b ; co2a co2b). Ideally, we'd
like to rewrite this to (co1a ; co2a) (co1b ; co2b). The problem is that
the resultant coercions might not be well kinded. Here is an example (things
labeled with x don't matter in this example):

  k1 :: Type
  k2 :: Type

  a :: k1 -> Type
  b :: k1

  h :: k1 ~ k2

  co1a :: x1 ~ (a |> (h -> <Type>)
  co1b :: x2 ~ (b |> h)

  co2a :: a ~ x3
  co2b :: b ~ x4

First, convince yourself of the following:

  co1a co1b :: x1 x2 ~ (a |> (h -> <Type>)) (b |> h)
  co2a co2b :: a b   ~ x3 x4

  (a |> (h -> <Type>)) (b |> h) `eqType` a b

That last fact is due to Note [Non-trivial definitional equality] in GHC.Core.TyCo.Rep,
where we ignore coercions in types as long as two types' kinds are the same.
In our case, we meet this last condition, because

  (a |> (h -> <Type>)) (b |> h) :: Type
    and
  a b :: Type

So the input coercion (co1a co1b ; co2a co2b) is well-formed. But the
suggested output coercions (co1a ; co2a) and (co1b ; co2b) are not -- the
kinds don't match up.

The solution here is to twiddle the kinds in the output coercions. First, we
need to find coercions

  ak :: kind(a |> (h -> <Type>)) ~ kind(a)
  bk :: kind(b |> h)             ~ kind(b)

This can be done with mkKindCo and buildCoercion. The latter assumes two
types are identical modulo casts and builds a coercion between them.

Then, we build (co1a ; co2a |> sym ak) and (co1b ; co2b |> sym bk) as the
output coercions. These are well-kinded.

Also, note that all of this is done after accumulated any nested AppCo
parameters. This step is to avoid quadratic behavior in calling coercionKind.

The problem described here was first found in dependent/should_compile/dynamic-paper.

-}

-- | Check to make sure that an AxInstCo is internally consistent.
-- Returns the conflicting branch, if it exists
-- See Note [Conflict checking with AxiomInstCo]
checkAxInstCo :: Coercion -> Maybe CoAxBranch
-- defined here to avoid dependencies in GHC.Core.Coercion
-- If you edit this function, you may need to update the GHC formalism
-- See Note [GHC Formalism] in GHC.Core.Lint
checkAxInstCo :: Coercion -> Maybe CoAxBranch
checkAxInstCo (AxiomInstCo CoAxiom Branched
ax Int
ind [Coercion]
cos)
  = let branch :: CoAxBranch
branch       = forall (br :: BranchFlag). CoAxiom br -> Int -> CoAxBranch
coAxiomNthBranch CoAxiom Branched
ax Int
ind
        tvs :: [CoVar]
tvs          = CoAxBranch -> [CoVar]
coAxBranchTyVars CoAxBranch
branch
        cvs :: [CoVar]
cvs          = CoAxBranch -> [CoVar]
coAxBranchCoVars CoAxBranch
branch
        incomps :: [CoAxBranch]
incomps      = CoAxBranch -> [CoAxBranch]
coAxBranchIncomps CoAxBranch
branch
        ([Type]
tys, [Type]
cotys) = forall b a. [b] -> [a] -> ([a], [a])
splitAtList [CoVar]
tvs (forall a b. (a -> b) -> [a] -> [b]
map Coercion -> Type
coercionLKind [Coercion]
cos)
        co_args :: [Coercion]
co_args      = forall a b. (a -> b) -> [a] -> [b]
map Type -> Coercion
stripCoercionTy [Type]
cotys
        subst :: TCvSubst
subst        = HasDebugCallStack => [CoVar] -> [Type] -> TCvSubst
zipTvSubst [CoVar]
tvs [Type]
tys TCvSubst -> TCvSubst -> TCvSubst
`composeTCvSubst`
                       HasDebugCallStack => [CoVar] -> [Coercion] -> TCvSubst
zipCvSubst [CoVar]
cvs [Coercion]
co_args
        target :: [Type]
target   = HasDebugCallStack => TCvSubst -> [Type] -> [Type]
Type.substTys TCvSubst
subst (CoAxBranch -> [Type]
coAxBranchLHS CoAxBranch
branch)
        in_scope :: InScopeSet
in_scope = TyCoVarSet -> InScopeSet
mkInScopeSet forall a b. (a -> b) -> a -> b
$
                   [TyCoVarSet] -> TyCoVarSet
unionVarSets (forall a b. (a -> b) -> [a] -> [b]
map ([Type] -> TyCoVarSet
tyCoVarsOfTypes forall b c a. (b -> c) -> (a -> b) -> a -> c
. CoAxBranch -> [Type]
coAxBranchLHS) [CoAxBranch]
incomps)
        flattened_target :: [Type]
flattened_target = InScopeSet -> [Type] -> [Type]
flattenTys InScopeSet
in_scope [Type]
target in
    [Type] -> [CoAxBranch] -> Maybe CoAxBranch
check_no_conflict [Type]
flattened_target [CoAxBranch]
incomps
  where
    check_no_conflict :: [Type] -> [CoAxBranch] -> Maybe CoAxBranch
    check_no_conflict :: [Type] -> [CoAxBranch] -> Maybe CoAxBranch
check_no_conflict [Type]
_    [] = forall a. Maybe a
Nothing
    check_no_conflict [Type]
flat (b :: CoAxBranch
b@CoAxBranch { cab_lhs :: CoAxBranch -> [Type]
cab_lhs = [Type]
lhs_incomp } : [CoAxBranch]
rest)
         -- See Note [Apartness] in GHC.Core.FamInstEnv
      | UnifyResultM TCvSubst
SurelyApart <- BindFun -> [Type] -> [Type] -> UnifyResultM TCvSubst
tcUnifyTysFG BindFun
alwaysBindFun [Type]
flat [Type]
lhs_incomp
      = [Type] -> [CoAxBranch] -> Maybe CoAxBranch
check_no_conflict [Type]
flat [CoAxBranch]
rest
      | Bool
otherwise
      = forall a. a -> Maybe a
Just CoAxBranch
b
checkAxInstCo Coercion
_ = forall a. Maybe a
Nothing


-----------
wrapSym :: SymFlag -> Coercion -> Coercion
wrapSym :: Bool -> Coercion -> Coercion
wrapSym Bool
sym Coercion
co | Bool
sym       = Coercion -> Coercion
mkSymCo Coercion
co
               | Bool
otherwise = Coercion
co

-- | Conditionally set a role to be representational
wrapRole :: ReprFlag
         -> Role         -- ^ current role
         -> Coercion -> Coercion
wrapRole :: Bool -> Role -> Coercion -> Coercion
wrapRole Bool
False Role
_       = forall a. a -> a
id
wrapRole Bool
True  Role
current = Role -> Role -> Coercion -> Coercion
downgradeRole Role
Representational Role
current

-- | If we require a representational role, return that. Otherwise,
-- return the "default" role provided.
chooseRole :: ReprFlag
           -> Role    -- ^ "default" role
           -> Role
chooseRole :: Bool -> Role -> Role
chooseRole Bool
True Role
_ = Role
Representational
chooseRole Bool
_    Role
r = Role
r

-----------
isAxiom_maybe :: Coercion -> Maybe (Bool, CoAxiom Branched, Int, [Coercion])
isAxiom_maybe :: Coercion -> Maybe (Bool, CoAxiom Branched, Int, [Coercion])
isAxiom_maybe (SymCo Coercion
co)
  | Just (Bool
sym, CoAxiom Branched
con, Int
ind, [Coercion]
cos) <- Coercion -> Maybe (Bool, CoAxiom Branched, Int, [Coercion])
isAxiom_maybe Coercion
co
  = forall a. a -> Maybe a
Just (Bool -> Bool
not Bool
sym, CoAxiom Branched
con, Int
ind, [Coercion]
cos)
isAxiom_maybe (AxiomInstCo CoAxiom Branched
con Int
ind [Coercion]
cos)
  = forall a. a -> Maybe a
Just (Bool
False, CoAxiom Branched
con, Int
ind, [Coercion]
cos)
isAxiom_maybe Coercion
_ = forall a. Maybe a
Nothing

matchAxiom :: Bool -- True = match LHS, False = match RHS
           -> CoAxiom br -> Int -> Coercion -> Maybe [Coercion]
matchAxiom :: forall (br :: BranchFlag).
Bool -> CoAxiom br -> Int -> Coercion -> Maybe [Coercion]
matchAxiom Bool
sym ax :: CoAxiom br
ax@(CoAxiom { co_ax_tc :: forall (br :: BranchFlag). CoAxiom br -> TyCon
co_ax_tc = TyCon
tc }) Int
ind Coercion
co
  | CoAxBranch { cab_tvs :: CoAxBranch -> [CoVar]
cab_tvs = [CoVar]
qtvs
               , cab_cvs :: CoAxBranch -> [CoVar]
cab_cvs = []   -- can't infer these, so fail if there are any
               , cab_roles :: CoAxBranch -> [Role]
cab_roles = [Role]
roles
               , cab_lhs :: CoAxBranch -> [Type]
cab_lhs = [Type]
lhs
               , cab_rhs :: CoAxBranch -> Type
cab_rhs = Type
rhs } <- forall (br :: BranchFlag). CoAxiom br -> Int -> CoAxBranch
coAxiomNthBranch CoAxiom br
ax Int
ind
  , Just LiftingContext
subst <- TyCoVarSet -> Type -> Coercion -> Maybe LiftingContext
liftCoMatch ([CoVar] -> TyCoVarSet
mkVarSet [CoVar]
qtvs)
                              (if Bool
sym then (TyCon -> [Type] -> Type
mkTyConApp TyCon
tc [Type]
lhs) else Type
rhs)
                              Coercion
co
  , forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (CoVar -> LiftingContext -> Bool
`isMappedByLC` LiftingContext
subst) [CoVar]
qtvs
  = forall (m :: * -> *) a b c.
Applicative m =>
(a -> b -> m c) -> [a] -> [b] -> m [c]
zipWithM (LiftingContext -> Role -> CoVar -> Maybe Coercion
liftCoSubstTyVar LiftingContext
subst) [Role]
roles [CoVar]
qtvs

  | Bool
otherwise
  = forall a. Maybe a
Nothing

-------------
compatible_co :: Coercion -> Coercion -> Bool
-- Check whether (co1 . co2) will be well-kinded
compatible_co :: Coercion -> Coercion -> Bool
compatible_co Coercion
co1 Coercion
co2
  = Type
x1 Type -> Type -> Bool
`eqType` Type
x2
  where
    x1 :: Type
x1 = Coercion -> Type
coercionRKind Coercion
co1
    x2 :: Type
x2 = Coercion -> Type
coercionLKind Coercion
co2

-------------
{-
etaForAllCo
~~~~~~~~~~~~~~~~~
(1) etaForAllCo_ty_maybe
Suppose we have

  g : all a1:k1.t1  ~  all a2:k2.t2

but g is *not* a ForAllCo. We want to eta-expand it. So, we do this:

  g' = all a1:(ForAllKindCo g).(InstCo g (a1 ~ a1 |> ForAllKindCo g))

Call the kind coercion h1 and the body coercion h2. We can see that

  h2 : t1 ~ t2[a2 |-> (a1 |> h1)]

According to the typing rule for ForAllCo, we get that

  g' : all a1:k1.t1  ~  all a1:k2.(t2[a2 |-> (a1 |> h1)][a1 |-> a1 |> sym h1])

or

  g' : all a1:k1.t1  ~  all a1:k2.(t2[a2 |-> a1])

as desired.

(2) etaForAllCo_co_maybe
Suppose we have

  g : all c1:(s1~s2). t1 ~ all c2:(s3~s4). t2

Similarly, we do this

  g' = all c1:h1. h2
     : all c1:(s1~s2). t1 ~ all c1:(s3~s4). t2[c2 |-> (sym eta1;c1;eta2)]
                                              [c1 |-> eta1;c1;sym eta2]

Here,

  h1   = mkNthCo Nominal 0 g :: (s1~s2)~(s3~s4)
  eta1 = mkNthCo r 2 h1      :: (s1 ~ s3)
  eta2 = mkNthCo r 3 h1      :: (s2 ~ s4)
  h2   = mkInstCo g (cv1 ~ (sym eta1;c1;eta2))
-}
etaForAllCo_ty_maybe :: Coercion -> Maybe (TyVar, Coercion, Coercion)
-- Try to make the coercion be of form (forall tv:kind_co. co)
etaForAllCo_ty_maybe :: Coercion -> Maybe (CoVar, Coercion, Coercion)
etaForAllCo_ty_maybe Coercion
co
  | Just (CoVar
tv, Coercion
kind_co, Coercion
r) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_ty_maybe Coercion
co
  = forall a. a -> Maybe a
Just (CoVar
tv, Coercion
kind_co, Coercion
r)

  | Pair Type
ty1 Type
ty2  <- Coercion -> Pair Type
coercionKind Coercion
co
  , Just (CoVar
tv1, Type
_) <- Type -> Maybe (CoVar, Type)
splitForAllTyVar_maybe Type
ty1
  , Type -> Bool
isForAllTy_ty Type
ty2
  , let kind_co :: Coercion
kind_co = HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
Nominal Int
0 Coercion
co
  = forall a. a -> Maybe a
Just ( CoVar
tv1, Coercion
kind_co
         , Coercion -> Coercion -> Coercion
mkInstCo Coercion
co (Role -> Type -> Coercion -> Coercion
mkGReflRightCo Role
Nominal (CoVar -> Type
TyVarTy CoVar
tv1) Coercion
kind_co))

  | Bool
otherwise
  = forall a. Maybe a
Nothing

etaForAllCo_co_maybe :: Coercion -> Maybe (CoVar, Coercion, Coercion)
-- Try to make the coercion be of form (forall cv:kind_co. co)
etaForAllCo_co_maybe :: Coercion -> Maybe (CoVar, Coercion, Coercion)
etaForAllCo_co_maybe Coercion
co
  | Just (CoVar
cv, Coercion
kind_co, Coercion
r) <- Coercion -> Maybe (CoVar, Coercion, Coercion)
splitForAllCo_co_maybe Coercion
co
  = forall a. a -> Maybe a
Just (CoVar
cv, Coercion
kind_co, Coercion
r)

  | Pair Type
ty1 Type
ty2  <- Coercion -> Pair Type
coercionKind Coercion
co
  , Just (CoVar
cv1, Type
_) <- Type -> Maybe (CoVar, Type)
splitForAllCoVar_maybe Type
ty1
  , Type -> Bool
isForAllTy_co Type
ty2
  = let kind_co :: Coercion
kind_co  = HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
Nominal Int
0 Coercion
co
        r :: Role
r        = CoVar -> Role
coVarRole CoVar
cv1
        l_co :: Coercion
l_co     = CoVar -> Coercion
mkCoVarCo CoVar
cv1
        kind_co' :: Coercion
kind_co' = Role -> Role -> Coercion -> Coercion
downgradeRole Role
r Role
Nominal Coercion
kind_co
        r_co :: Coercion
r_co     = (Coercion -> Coercion
mkSymCo (HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r Int
2 Coercion
kind_co')) Coercion -> Coercion -> Coercion
`mkTransCo`
                   Coercion
l_co Coercion -> Coercion -> Coercion
`mkTransCo`
                   (HasDebugCallStack => Role -> Int -> Coercion -> Coercion
mkNthCo Role
r Int
3 Coercion
kind_co')
    in forall a. a -> Maybe a
Just ( CoVar
cv1, Coercion
kind_co
            , Coercion -> Coercion -> Coercion
mkInstCo Coercion
co (Role -> Coercion -> Coercion -> Coercion -> Coercion
mkProofIrrelCo Role
Nominal Coercion
kind_co Coercion
l_co Coercion
r_co))

  | Bool
otherwise
  = forall a. Maybe a
Nothing

etaAppCo_maybe :: Coercion -> Maybe (Coercion,Coercion)
-- If possible, split a coercion
--   g :: t1a t1b ~ t2a t2b
-- into a pair of coercions (left g, right g)
etaAppCo_maybe :: Coercion -> Maybe (Coercion, Coercion)
etaAppCo_maybe Coercion
co
  | Just (Coercion
co1,Coercion
co2) <- Coercion -> Maybe (Coercion, Coercion)
splitAppCo_maybe Coercion
co
  = forall a. a -> Maybe a
Just (Coercion
co1,Coercion
co2)
  | (Pair Type
ty1 Type
ty2, Role
Nominal) <- Coercion -> (Pair Type, Role)
coercionKindRole Coercion
co
  , Just (Type
_,Type
t1) <- Type -> Maybe (Type, Type)
splitAppTy_maybe Type
ty1
  , Just (Type
_,Type
t2) <- Type -> Maybe (Type, Type)
splitAppTy_maybe Type
ty2
  , let isco1 :: Bool
isco1 = Type -> Bool
isCoercionTy Type
t1
  , let isco2 :: Bool
isco2 = Type -> Bool
isCoercionTy Type
t2
  , Bool
isco1 forall a. Eq a => a -> a -> Bool
== Bool
isco2
  = forall a. a -> Maybe a
Just (LeftOrRight -> Coercion -> Coercion
LRCo LeftOrRight
CLeft Coercion
co, LeftOrRight -> Coercion -> Coercion
LRCo LeftOrRight
CRight Coercion
co)
  | Bool
otherwise
  = forall a. Maybe a
Nothing

etaTyConAppCo_maybe :: TyCon -> Coercion -> Maybe [Coercion]
-- If possible, split a coercion
--       g :: T s1 .. sn ~ T t1 .. tn
-- into [ Nth 0 g :: s1~t1, ..., Nth (n-1) g :: sn~tn ]
etaTyConAppCo_maybe :: TyCon -> Coercion -> Maybe [Coercion]
etaTyConAppCo_maybe TyCon
tc (TyConAppCo Role
_ TyCon
tc2 [Coercion]
cos2)
  = forall a. HasCallStack => Bool -> a -> a
assert (TyCon
tc forall a. Eq a => a -> a -> Bool
== TyCon
tc2) forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just [Coercion]
cos2

etaTyConAppCo_maybe TyCon
tc Coercion
co
  | Bool -> Bool
not (TyCon -> Bool
mustBeSaturated TyCon
tc)
  , (Pair Type
ty1 Type
ty2, Role
r) <- Coercion -> (Pair Type, Role)
coercionKindRole Coercion
co
  , Just (TyCon
tc1, [Type]
tys1)  <- HasDebugCallStack => Type -> Maybe (TyCon, [Type])
splitTyConApp_maybe Type
ty1
  , Just (TyCon
tc2, [Type]
tys2)  <- HasDebugCallStack => Type -> Maybe (TyCon, [Type])
splitTyConApp_maybe Type
ty2
  , TyCon
tc1 forall a. Eq a => a -> a -> Bool
== TyCon
tc2
  , TyCon -> Role -> Bool
isInjectiveTyCon TyCon
tc Role
r  -- See Note [NthCo and newtypes] in GHC.Core.TyCo.Rep
  , let n :: Int
n = forall (t :: * -> *) a. Foldable t => t a -> Int
length [Type]
tys1
  , [Type]
tys2 forall a. [a] -> Int -> Bool
`lengthIs` Int
n      -- This can fail in an erroneous program
                           -- E.g. T a ~# T a b
                           -- #14607
  = forall a. HasCallStack => Bool -> a -> a
assert (TyCon
tc forall a. Eq a => a -> a -> Bool
== TyCon
tc1) forall a b. (a -> b) -> a -> b
$
    forall a. a -> Maybe a
Just (Int -> Coercion -> [Role] -> [Coercion]
decomposeCo Int
n Coercion
co (Role -> TyCon -> [Role]
tyConRolesX Role
r TyCon
tc1))
    -- NB: n might be <> tyConArity tc
    -- e.g.   data family T a :: * -> *
    --        g :: T a b ~ T c d

  | Bool
otherwise
  = forall a. Maybe a
Nothing

{-
Note [Eta for AppCo]
~~~~~~~~~~~~~~~~~~~~
Suppose we have
   g :: s1 t1 ~ s2 t2

Then we can't necessarily make
   left  g :: s1 ~ s2
   right g :: t1 ~ t2
because it's possible that
   s1 :: * -> *         t1 :: *
   s2 :: (*->*) -> *    t2 :: * -> *
and in that case (left g) does not have the same
kind on either side.

It's enough to check that
  kind t1 = kind t2
because if g is well-kinded then
  kind (s1 t2) = kind (s2 t2)
and these two imply
  kind s1 = kind s2

-}

optForAllCoBndr :: LiftingContext -> Bool
                -> TyCoVar -> Coercion -> (LiftingContext, TyCoVar, Coercion)
optForAllCoBndr :: LiftingContext
-> Bool -> CoVar -> Coercion -> (LiftingContext, CoVar, Coercion)
optForAllCoBndr LiftingContext
env Bool
sym
  = Bool
-> (Coercion -> Coercion)
-> LiftingContext
-> CoVar
-> Coercion
-> (LiftingContext, CoVar, Coercion)
substForAllCoBndrUsingLC Bool
sym (LiftingContext -> Bool -> Bool -> Role -> Coercion -> Coercion
opt_co4_wrap LiftingContext
env Bool
sym Bool
False Role
Nominal) LiftingContext
env