plum

#treesitter#compiler#wasm

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
+ }