unusedInjTvsInRHS :: TyCon -> [Bool] -> [Type] -> Type -> Pair TyVarSet
-- INVARIANT: [Bool] list contains at least one True value
-- See Note [Verifying injectivity annotation]. This function implements fourth
-- check described there.
-- In theory, instead of implementing this whole check in this way, we could
-- attempt to unify equation with itself.  We would reject exactly the same
-- equations but this method gives us more precise error messages by returning
-- precise names of variables that are not mentioned in the RHS.
unusedInjTvsInRHS tycon injList lhs rhs =
  (`minusVarSet` injRhsVars) <$> injLHSVars
    where
      -- set of type and kind variables in which type family is injective
      (invis_pairs, vis_pairs)
        = partitionInvisibles tycon snd (zipEqual "unusedInjTvsInRHS" injList lhs)
      invis_lhs = uncurry filterByList $ unzip invis_pairs
      vis_lhs   = uncurry filterByList $ unzip vis_pairs

      invis_vars = tyCoVarsOfTypes invis_lhs
      Pair invis_vars' vis_vars = splitVisVarsOfTypes vis_lhs
      injLHSVars
        = Pair (invis_vars `minusVarSet` vis_vars `unionVarSet` invis_vars')
               vis_vars

      -- set of type variables appearing in the RHS on an injective position.
      -- For all returned variables we assume their associated kind variables
      -- also appear in the RHS.
      injRhsVars = collectInjVars rhs

      -- Collect all type variables that are either arguments to a type
      -- constructor or to injective type families.
      collectInjVars :: Type -> VarSet
      collectInjVars (TyVarTy v)
        = unitVarSet v `unionVarSet` collectInjVars (tyVarKind v)
      collectInjVars (TyConApp tc tys)
        | isTypeFamilyTyCon tc = collectInjTFVars tys
                                                 (familyTyConInjectivityInfo tc)
        | otherwise            = mapUnionVarSet collectInjVars tys
      collectInjVars (LitTy {})
        = emptyVarSet
      collectInjVars (ForAllTy (Anon arg) res)
        = collectInjVars arg `unionVarSet` collectInjVars res
      collectInjVars (AppTy fun arg)
        = collectInjVars fun `unionVarSet` collectInjVars arg
      -- no forall types in the RHS of a type family
      collectInjVars (ForAllTy _ _)    =
          panic "unusedInjTvsInRHS.collectInjVars"
      collectInjVars (CastTy ty _)   = collectInjVars ty
      collectInjVars (CoercionTy {}) = emptyVarSet

      collectInjTFVars :: [Type] -> Injectivity -> VarSet
      collectInjTFVars _ NotInjective
          = emptyVarSet
      collectInjTFVars tys (Injective injList)
          = mapUnionVarSet collectInjVars (filterByList injList tys)


-- | Is type headed by a type family application?
unusedInjTvsInRHS :: TyCon -> [Bool] -> [Type] -> Type -> Pair TyVarSet
unusedInjTvsInRHS tycon injList lhs rhs =
  (`minusVarSet` injRhsVars) <$> injLHSVars
    where
      -- set of type and kind variables in which type family is injective
      (invis_pairs, vis_pairs)
        = partitionInvisibles tycon snd (zipEqual "unusedInjTvsInRHS" injList lhs)
      invis_lhs = uncurry filterByList $ unzip invis_pairs
      vis_lhs   = uncurry filterByList $ unzip vis_pairs

      invis_vars = tyCoVarsOfTypes invis_lhs
      Pair invis_vars' vis_vars = splitVisVarsOfTypes vis_lhs
      injLHSVars
        = Pair (invis_vars `minusVarSet` vis_vars `unionVarSet` invis_vars')
               vis_vars

      -- set of type variables appearing in the RHS on an injective position.
      -- For all returned variables we assume their associated kind variables
      -- also appear in the RHS.
      injRhsVars = collectInjVars rhs

      -- Collect all type variables that are either arguments to a type
      -- constructor or to injective type families.
      collectInjVars :: Type -> VarSet
      collectInjVars (TyVarTy v)
        = unitVarSet v `unionVarSet` collectInjVars (tyVarKind v)
      collectInjVars (TyConApp tc tys)
        | isTypeFamilyTyCon tc = collectInjTFVars tys
                                                 (familyTyConInjectivityInfo tc)
        | otherwise            = mapUnionVarSet collectInjVars tys
      collectInjVars (LitTy {})
        = emptyVarSet
      collectInjVars (ForAllTy (Anon arg) res)
        = collectInjVars arg `unionVarSet` collectInjVars res
      collectInjVars (AppTy fun arg)
        = collectInjVars fun `unionVarSet` collectInjVars arg
      -- no forall types in the RHS of a type family
      collectInjVars (ForAllTy _ _)    =
          panic "unusedInjTvsInRHS.collectInjVars"
      collectInjVars (CastTy ty _)   = collectInjVars ty
      collectInjVars (CoercionTy {}) = emptyVarSet

      collectInjTFVars :: [Type] -> Injectivity -> VarSet
      collectInjTFVars _ NotInjective
          = emptyVarSet
      collectInjTFVars tys (Injective injList)
          = mapUnionVarSet collectInjVars (filterByList injList tys)


-- | Is type headed by a type family application?
unusedInjTvsInRHS tycon injList lhs rhs =
  (`minusVarSet` injRhsVars) <$> injLHSVars
    where
      -- set of type and kind variables in which type family is injective
      (invis_pairs, vis_pairs)
        = partitionInvisibles tycon snd (zipEqual "unusedInjTvsInRHS" injList lhs)
      invis_lhs = uncurry filterByList $ unzip invis_pairs
      vis_lhs   = uncurry filterByList $ unzip vis_pairs

      invis_vars = tyCoVarsOfTypes invis_lhs
      Pair invis_vars' vis_vars = splitVisVarsOfTypes vis_lhs
      injLHSVars
        = Pair (invis_vars `minusVarSet` vis_vars `unionVarSet` invis_vars')
               vis_vars

      -- set of type variables appearing in the RHS on an injective position.
      -- For all returned variables we assume their associated kind variables
      -- also appear in the RHS.
      injRhsVars = collectInjVars rhs

      -- Collect all type variables that are either arguments to a type
      -- constructor or to injective type families.
      collectInjVars :: Type -> VarSet
      collectInjVars (TyVarTy v)
        = unitVarSet v `unionVarSet` collectInjVars (tyVarKind v)
      collectInjVars (TyConApp tc tys)
        | isTypeFamilyTyCon tc = collectInjTFVars tys
                                                 (familyTyConInjectivityInfo tc)
        | otherwise            = mapUnionVarSet collectInjVars tys
      collectInjVars (LitTy {})
        = emptyVarSet
      collectInjVars (ForAllTy (Anon arg) res)
        = collectInjVars arg `unionVarSet` collectInjVars res
      collectInjVars (AppTy fun arg)
        = collectInjVars fun `unionVarSet` collectInjVars arg
      -- no forall types in the RHS of a type family
      collectInjVars (ForAllTy _ _)    =
          panic "unusedInjTvsInRHS.collectInjVars"
      collectInjVars (CastTy ty _)   = collectInjVars ty
      collectInjVars (CoercionTy {}) = emptyVarSet

      collectInjTFVars :: [Type] -> Injectivity -> VarSet
      collectInjTFVars _ NotInjective
          = emptyVarSet
      collectInjTFVars tys (Injective injList)
          = mapUnionVarSet collectInjVars (filterByList injList tys)


-- | Is type headed by a type family application?