plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
69aa8c3
— Peter John
2026-11-01T17:14:13+05:30
fix: recovered call failures don't reposition errors; const-shaped patterns on non-enums rejected
plum-checker/src/lib.rs
CHANGED
|
@@ -2957,6 +2957,8 @@ fn checkPattern(
|
|
|
2957
2957
|
&& ctx.enum_variants.contains_key(n);
|
|
2958
2958
|
if is_known_variant {
|
|
2959
2959
|
Ok(()) // equality check against a known enum tag, e.g. `Red`
|
|
2960
|
+
} else if n.chars().next().is_some_and(|c| c.is_uppercase()) {
|
|
2961
|
+
Err(format!("'{}' is not a variant of {}", n, subject_ty))
|
|
2960
2962
|
} else {
|
|
2961
2963
|
env.insert(n.clone(), TypeScheme::mono(subject_ty.clone()));
|
|
2962
2964
|
Ok(())
|
|
@@ -3496,7 +3498,12 @@ pub fn inferExprWith(
|
|
|
3496
3498
|
if let Some(pos) = call_pos.filter(|p| p.0 != 0) {
|
|
3497
3499
|
CUR_POS.with(|p| p.set(pos));
|
|
3498
3500
|
}
|
|
3501
|
+
let entry_err_pos = ERR_POS.with(|e| e.get());
|
|
3499
3502
|
let r = inferExprInner(expr, env, ctx, expected);
|
|
3503
|
+
if r.is_ok() {
|
|
3504
|
+
// Any failure inside was recovered from; it must not mark the statement.
|
|
3505
|
+
ERR_POS.with(|e| e.set(entry_err_pos));
|
|
3506
|
+
}
|
|
3500
3507
|
if r.is_err() && call_pos.is_some_and(|p| p.0 != 0) && ERR_POS.with(|e| e.get()).is_none() {
|
|
3501
3508
|
ERR_POS.with(|e| e.set(call_pos));
|
|
3502
3509
|
}
|
plum-checker/tests/checker_tests.rs
CHANGED
|
@@ -2132,3 +2132,28 @@ fn unknown_method_on_heap_const_is_checker_error() {
|
|
|
2132
2132
|
assert_eq!(assign[0].line, 13, "{:?}", assign);
|
|
2133
2133
|
assert!(checkSrc(&src("Cfg.CELL.get()")).is_ok());
|
|
2134
2134
|
}
|
|
2135
|
+
|
|
2136
|
+
#[test]
|
|
2137
|
+
fn recovered_call_failure_does_not_reposition_genuine_error() {
|
|
2138
|
+
let src = "enum Box[T] =\n | Box(v: T)\n\nenum Cfg =\n | Cfg(a: Int)\n\n CELL = Box(1)\n\nfun f() -> Int =\n return \"{Cfg.CELL.size()}\"\n";
|
|
2139
|
+
let errs = checkSrc(src).unwrap_err();
|
|
2140
|
+
let e = errs.iter().find(|e| e.message.contains("return type mismatch")).unwrap();
|
|
2141
|
+
assert_eq!((e.line, e.column), (9, 1), "{:?}", errs);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
#[test]
|
|
2145
|
+
fn const_shaped_name_pattern_on_int_is_rejected() {
|
|
2146
|
+
for src in [
|
|
2147
|
+
"fun f(n: Int) -> Int =\n match n\n MAX => 1\n _ => 0\n",
|
|
2148
|
+
"fun f(n: Int) -> Int =\n if MAX := n\n return 1\n 0\n",
|
|
2149
|
+
] {
|
|
2150
|
+
let msg = errText(checkSrc(src));
|
|
2151
|
+
assert!(msg.contains("'MAX' is not a variant of Int"), "{msg}");
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
#[test]
|
|
2156
|
+
fn digit_variant_pattern_still_works() {
|
|
2157
|
+
let src = "enum V2 =\n | A1(n: Int)\n | B2\n\nfun f(w: V2) -> Int =\n match w\n A1(n) => n\n B2 => 0\n\nfun g(w: V2) -> Int =\n if B2 := w\n return 1\n 0\n";
|
|
2158
|
+
assert!(checkSrc(src).is_ok());
|
|
2159
|
+
}
|