mkTcTransAppCo :: Role           -- ^ r1
               -> TcCoercion     -- ^ co1 :: ty1a ~r1 ty1b
               -> TcType         -- ^ ty1a
               -> TcType         -- ^ ty1b
               -> Role           -- ^ r2
               -> TcCoercion     -- ^ co2 :: ty2a ~r2 ty2b
               -> TcType         -- ^ ty2a
               -> TcType         -- ^ ty2b
               -> Role           -- ^ r3
               -> TcCoercion     -- ^ :: ty1a ty2a ~r3 ty1b ty2b
mkTcTransAppCo r1 co1 ty1a ty1b r2 co2 ty2a ty2b r3
-- How incredibly fiddly! Is there a better way??
  = case (r1, r2, r3) of
      (_,                _,                Phantom)
        -> mkTcPhantomCo (mkAppTy ty1a ty2a) (mkAppTy ty1b ty2b)
      (_,                _,                Nominal)
        -> ASSERT( r1 == Nominal && r2 == Nominal )
           mkTcAppCo co1 co2
      (Nominal,          Nominal,          Representational)
        -> mkTcSubCo (mkTcAppCo co1 co2)
      (_,                Nominal,          Representational)
        -> ASSERT( r1 == Representational )
           mkTcAppCo co1 co2
      (Nominal,          Representational, Representational)
        -> go (mkTcSubCo co1)
      (_               , _,                Representational)
        -> ASSERT( r1 == Representational && r2 == Representational )
           go co1
  where
    go co1_repr
      | Just (tc1b, tys1b) <- tcSplitTyConApp_maybe ty1b
      , nextRole ty1b == r2
      = (co1_repr `mkTcAppCo` mkTcNomReflCo ty2a) `mkTcTransCo`
        (mkTcTyConAppCo Representational tc1b
           (zipWith mkTcReflCo (tyConRolesX Representational tc1b) tys1b
            ++ [co2]))

      | Just (tc1a, tys1a) <- tcSplitTyConApp_maybe ty1a
      , nextRole ty1a == r2
      = (mkTcTyConAppCo Representational tc1a
           (zipWith mkTcReflCo (tyConRolesX Representational tc1a) tys1a
            ++ [co2]))
        `mkTcTransCo`
        (co1_repr `mkTcAppCo` mkTcNomReflCo ty2b)

      | otherwise
      = pprPanic "mkTcTransAppCo" (vcat [ ppr r1, ppr co1, ppr ty1a, ppr ty1b
                                        , ppr r2, ppr co2, ppr ty2a, ppr ty2b
                                        , ppr r3 ])
mkTcTransAppCo :: Role           -- ^ r1
               -> TcCoercion     -- ^ co1 :: ty1a ~r1 ty1b
               -> TcType         -- ^ ty1a
               -> TcType         -- ^ ty1b
               -> Role           -- ^ r2
               -> TcCoercion     -- ^ co2 :: ty2a ~r2 ty2b
               -> TcType         -- ^ ty2a
               -> TcType         -- ^ ty2b
               -> Role           -- ^ r3
               -> TcCoercion
mkTcTransAppCo r1 co1 ty1a ty1b r2 co2 ty2a ty2b r3
-- How incredibly fiddly! Is there a better way??
  = case (r1, r2, r3) of
      (_,                _,                Phantom)
        -> mkTcPhantomCo (mkAppTy ty1a ty2a) (mkAppTy ty1b ty2b)
      (_,                _,                Nominal)
        -> ASSERT( r1 == Nominal && r2 == Nominal )
           mkTcAppCo co1 co2
      (Nominal,          Nominal,          Representational)
        -> mkTcSubCo (mkTcAppCo co1 co2)
      (_,                Nominal,          Representational)
        -> ASSERT( r1 == Representational )
           mkTcAppCo co1 co2
      (Nominal,          Representational, Representational)
        -> go (mkTcSubCo co1)
      (_               , _,                Representational)
        -> ASSERT( r1 == Representational && r2 == Representational )
           go co1
  where
    go co1_repr
      | Just (tc1b, tys1b) <- tcSplitTyConApp_maybe ty1b
      , nextRole ty1b == r2
      = (co1_repr `mkTcAppCo` mkTcNomReflCo ty2a) `mkTcTransCo`
        (mkTcTyConAppCo Representational tc1b
           (zipWith mkTcReflCo (tyConRolesX Representational tc1b) tys1b
            ++ [co2]))

      | Just (tc1a, tys1a) <- tcSplitTyConApp_maybe ty1a
      , nextRole ty1a == r2
      = (mkTcTyConAppCo Representational tc1a
           (zipWith mkTcReflCo (tyConRolesX Representational tc1a) tys1a
            ++ [co2]))
        `mkTcTransCo`
        (co1_repr `mkTcAppCo` mkTcNomReflCo ty2b)

      | otherwise
      = pprPanic "mkTcTransAppCo" (vcat [ ppr r1, ppr co1, ppr ty1a, ppr ty1b
                                        , ppr r2, ppr co2, ppr ty2a, ppr ty2b
                                        , ppr r3 ])
mkTcTransAppCo r1 co1 ty1a ty1b r2 co2 ty2a ty2b r3
-- How incredibly fiddly! Is there a better way??
  = case (r1, r2, r3) of
      (_,                _,                Phantom)
        -> mkTcPhantomCo (mkAppTy ty1a ty2a) (mkAppTy ty1b ty2b)
      (_,                _,                Nominal)
        -> ASSERT( r1 == Nominal && r2 == Nominal )
           mkTcAppCo co1 co2
      (Nominal,          Nominal,          Representational)
        -> mkTcSubCo (mkTcAppCo co1 co2)
      (_,                Nominal,          Representational)
        -> ASSERT( r1 == Representational )
           mkTcAppCo co1 co2
      (Nominal,          Representational, Representational)
        -> go (mkTcSubCo co1)
      (_               , _,                Representational)
        -> ASSERT( r1 == Representational && r2 == Representational )
           go co1
  where
    go co1_repr
      | Just (tc1b, tys1b) <- tcSplitTyConApp_maybe ty1b
      , nextRole ty1b == r2
      = (co1_repr `mkTcAppCo` mkTcNomReflCo ty2a) `mkTcTransCo`
        (mkTcTyConAppCo Representational tc1b
           (zipWith mkTcReflCo (tyConRolesX Representational tc1b) tys1b
            ++ [co2]))

      | Just (tc1a, tys1a) <- tcSplitTyConApp_maybe ty1a
      , nextRole ty1a == r2
      = (mkTcTyConAppCo Representational tc1a
           (zipWith mkTcReflCo (tyConRolesX Representational tc1a) tys1a
            ++ [co2]))
        `mkTcTransCo`
        (co1_repr `mkTcAppCo` mkTcNomReflCo ty2b)

      | otherwise
      = pprPanic "mkTcTransAppCo" (vcat [ ppr r1, ppr co1, ppr ty1a, ppr ty1b
                                        , ppr r2, ppr co2, ppr ty2a, ppr ty2b
                                        , ppr r3 ])