350b7b49b6
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.
79 lines
2.4 KiB
Rust
79 lines
2.4 KiB
Rust
use myc::ast::environment::Environment;
|
|
use myc::ast::types::Value;
|
|
|
|
#[test]
|
|
fn test_series_api_with_limit() {
|
|
let env = Environment::new();
|
|
// (series limit) — schema is inferred from push calls
|
|
let source = "(series 100)";
|
|
let result = env.compile(source).into_result();
|
|
assert!(result.is_ok(), "Series creation with limit should work: {:?}", result.err());
|
|
}
|
|
|
|
/// Regression test for HM type inference through nested closures.
|
|
///
|
|
/// `series` is created in an outer closure, `push` happens in an inner closure.
|
|
/// The pushed value type must propagate back through the TypeVar chain so that
|
|
/// the compiler can inject the `:float` schema — no explicit schema given.
|
|
#[test]
|
|
fn test_series_infer_type_from_nested_closure() {
|
|
let env = Environment::new();
|
|
let source = r#"
|
|
(do
|
|
(def make-acc
|
|
(fn [n]
|
|
(do
|
|
(def s (series n))
|
|
(def total 0.0)
|
|
(fn [x]
|
|
(do
|
|
(assign total (+ total x))
|
|
(push s x)
|
|
(s 0))))))
|
|
(def acc (make-acc 5))
|
|
(acc 1.5))
|
|
"#;
|
|
let result = env.run_script(source);
|
|
match result {
|
|
Ok(Value::Float(v)) if (v - 1.5).abs() < 1e-9 => {}
|
|
other => panic!("Expected Float(1.5), got {:?}", other),
|
|
}
|
|
}
|
|
|
|
/// 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();
|
|
// Known design choice: currently order dependent.
|
|
let source = "(= {:a 1 :b 2} {:b 2 :a 1})";
|
|
let result = env.run_script(source);
|
|
match result {
|
|
Ok(Value::Bool(false)) => {},
|
|
_ => panic!("Expected false (current implementation behavior)"),
|
|
}
|
|
}
|