Fix: Apply substitutions to upvalue types

When an identifier is used as an upvalue within a nested scope (e.g., a
`while` loop), the type checker needs to apply the global substitution
map to resolve `TypeVar`s that might have been determined in outer
scopes. This ensures that the correct type is used even if
`ctx.set_type` couldn't propagate the change directly through the
upvalue address.

This fix also includes a regression test to specifically cover this
scenario with series and closures.
This commit is contained in:
2026-03-28 14:22:25 +01:00
parent 9c85fb605b
commit 350b7b49b6
2 changed files with 30 additions and 2 deletions
+5 -2
View File
@@ -906,7 +906,10 @@ impl TypeChecker {
NodeKind::Identifier { symbol, binding } => {
if let IdentifierBinding::Reference(addr) = binding {
let ty = ctx.get_type(*addr);
// Apply the current HM substitution so that TypeVars resolved in
// nested scopes (e.g. inside a `while` body) are visible here even
// when ctx.set_type could not propagate back through an upvalue address.
let ty = Self::apply_subst(ctx.get_type(*addr), &self.subst.borrow());
(
NodeKind::Identifier {
symbol: symbol.clone(),
@@ -915,7 +918,7 @@ impl TypeChecker {
ty,
)
} else if let IdentifierBinding::Declaration { addr, kind: decl_kind } = binding {
let ty = ctx.get_type(*addr);
let ty = Self::apply_subst(ctx.get_type(*addr), &self.subst.borrow());
(
NodeKind::Identifier {
symbol: symbol.clone(),