Note [Linting type lets]
In the desugarer, it's very very convenient to be able to say (in effect)
let a = Type Bool in
let x::a = True in <body>
That is, use a type let. See Note [Core type and coercion invariant] in "GHC.Core".
One place it is used is in mkWwBodies; see Note [Join points and beta-redexes]
in GHC.Core.Opt.WorkWrap.Utils. (Maybe there are other "clients" of this feature; I'm not sure).
* Hence when linting <body> we need to remember that a=Int, else we
might reject a correct program. So we carry a type substitution (in
this example [a -> Bool]) and apply this substitution before
comparing types. In effect, in Lint, type equality is always
equality-modulo-le-subst. This is in the le_subst field of
LintEnv. But nota bene:
(SI1) The le_subst substitution is applied to types and coercions only
(SI2) The result of that substitution is used only to check for type
equality, to check well-typed-ness, /but is then discarded/.
The result of substitution does not outlive the CoreLint pass.
(SI3) The InScopeSet of le_subst includes only TyVar and CoVar binders.
* The function
lintInTy :: Type -> LintM (Type, Kind)
returns a substituted type.
* When we encounter a binder (like x::a) we must apply the substitution
to the type of the binding variable. lintBinders does this.
* Clearly we need to clone tyvar binders as we go.
* But take care (#17590)! We must also clone CoVar binders:
let a = TYPE (ty |> cv)
in \cv -> blah
blindly substituting for `a` might capture `cv`.
* Alas, when cloning a coercion variable we might choose a unique
that happens to clash with an inner Id, thus
\cv_66 -> let wild_X7 = blah in blah
We decide to clone `cv_66` because it's already in scope. Fine,
choose a new unique. Aha, X7 looks good. So we check the lambda
body with le_subst of [cv_66 :-> cv_X7]
This is all fine, even though we use the same unique as wild_X7.
As (SI2) says, we do /not/ return a new lambda
(\cv_X7 -> let wild_X7 = blah in ...)
We simply use the le_subst substitution in types/coercions only, when
checking for equality.
* We still need to check that Id occurrences are bound by some
enclosing binding. We do /not/ use the InScopeSet for the le_subst
for this purpose -- it contains only TyCoVars. Instead we have a separate
le_ids for the in-scope Id binders.
Sigh. We might want to explore getting rid of type-let! References 2
- Join points and beta-redexes GHC.Core.Opt.WorkWrap.Utils
- Core type and coercion invariant GHC.Core
Referenced by 2
- GHC.Core.Lint call site ×2