Thank you for the paper, it helped with my understanding of how it's supposed to work.
-- Note [Generating the in-scope set for a substitution]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- If we want to substitute [a -> ty1, b -> ty2] I used to
-- think it was enough to generate an in-scope set that includes
-- fv(ty1,ty2). But that's not enough; we really should also take the
-- free vars of the type we are substituting into! Example:
-- (forall b. (a,b,x)) [a -> List b]
-- Then if we use the in-scope set {b}, there is a danger we will rename
-- the forall'd variable to 'x' by mistake, getting this:
-- (forall x. (List b, x, x)
-- Urk! This means looking at all the calls to mkOpenTvSubst....
Currently the InScope set only contains the free variables of the arguments when linting type application [2][3][4] and doesn't contain the free variables of the body that it's substituting in.
When I changed it to include the free variables of the body my core lint error went away:
substTyWith :: [TyVar] -> [Type] -> Type -> Type
substTyWith tvs tys ty = ASSERT( length tvs == length tys )
substTy (extendTCvInScopeList (zipOpenTCvSubst tvs tys) (tyCoVarsOfTypeList ty)) ty
It seems unlikely to me that this is the issue, since this code is very old, but I don't have a better explanation for this and a second pair of eyes would help.