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:
@@ -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(),
|
||||
|
||||
@@ -40,6 +40,31 @@ fn test_series_infer_type_from_nested_closure() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Regression test: TypeVar on a series used as an upvalue inside a `while` loop
|
||||
/// (zero-parameter lambda created by the macro) must be resolved via the global
|
||||
/// substitution even when `ctx.set_type` silently ignores the upvalue address.
|
||||
#[test]
|
||||
fn test_series_typevar_resolved_through_while_upvalue() {
|
||||
let env = Environment::new();
|
||||
let source = r#"
|
||||
(do
|
||||
(def ticks (series 10))
|
||||
(def i 0)
|
||||
(while (< i 3)
|
||||
(do
|
||||
(push ticks {:price (* i 1.0)})
|
||||
(assign i (+ i 1))))
|
||||
(def p (.price ticks))
|
||||
(p 0)
|
||||
)
|
||||
"#;
|
||||
let result = env.run_script(source);
|
||||
match result {
|
||||
Ok(Value::Float(v)) if (v - 2.0).abs() < 1e-9 => {}
|
||||
other => panic!("Expected Float(2.0), got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_structural_equality_order() {
|
||||
let env = Environment::new();
|
||||
|
||||
Reference in New Issue
Block a user