plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


6580245 — Peter John 2026-11-01T20:41:46+05:30
T7: checker unions.rs (restorePlan, optional/err member wrap)
Files changed (1) hide show
  1. plum-checker/src/unions.rs +43 -1
plum-checker/src/unions.rs CHANGED
@@ -22,6 +22,16 @@ pub fn nested<R>(f: impl FnOnce() -> R) -> R {
22
22
  r
23
23
  }
24
24
 
25
+ /// Runs `f` (the success type of an `!T`/`T?` slot) at the top level again: a
26
+ /// member converts to a union enum there, though the error union itself is a
27
+ /// nested position for everything else.
28
+ pub fn atTop<R>(f: impl FnOnce() -> R) -> R {
29
+ let saved = DEPTH.with(|d| d.replace(0));
30
+ let r = f();
31
+ DEPTH.with(|d| d.set(saved));
32
+ r
33
+ }
34
+
25
35
  fn enumOf(ty: &PlumType) -> Option<(&str, &[PlumType])> {
26
36
  match ty {
27
37
  PlumType::TNamed(n) => Some((n, &[])),
@@ -85,7 +95,10 @@ pub fn wrapVariantFor(expected: &PlumType, actual: &PlumType, ctx: &CheckCtx) ->
85
95
 
86
96
  /// Records a wrap when `expr` (of type `actual`) lands where `expected` is a union enum.
87
97
  pub fn recordMemberWrap(expr: &ast::Expr, actual: &PlumType, expected: Option<&PlumType>, ctx: &CheckCtx) {
88
- let Some(expected) = expected else { return };
98
+ let Some(mut expected) = expected else { return };
99
+ while let PlumType::TErr(inner, _) = expected {
100
+ expected = inner;
101
+ }
89
102
  if let Some(v) = wrapVariantFor(expected, actual, ctx) {
90
103
  plan::recordWrap(expr as *const ast::Expr as usize, v);
91
104
  }
@@ -348,6 +361,13 @@ pub fn dispatchSig(
348
361
  let Some(sig) = memberSig(&mt, name, ctx) else {
349
362
  return Some(Err(format!("{en} has no method '{name}': member {label} doesn't define it")));
350
363
  };
364
+ if let PlumType::TFun(ps, _) = &sig {
365
+ if ps.iter().any(|p| matches!(p, PlumType::TVariadic(_))) {
366
+ return Some(Err(format!(
367
+ "{en}.{name}: auto-dispatch can't forward a variadic parameter; define {name} on {en}"
368
+ )));
369
+ }
370
+ }
351
371
  match &first {
352
372
  None => first = Some((label, sig)),
353
373
  Some((l0, s0)) => {
@@ -382,3 +402,25 @@ pub fn recordInterpolation(e: &ast::Expr, ty: &PlumType, ctx: &CheckCtx) {
382
402
  }
383
403
  plan::recordDispatch(e as *const ast::Expr as usize, out);
384
404
  }
405
+
406
+ /// Puts `p` back into the recorder (after a re-check consumed it). Addresses
407
+ /// stay those of the checked program; narrowing names are renumbered in order.
408
+ pub fn restorePlan(p: &plan::UnionPlan) {
409
+ plan::beginPlan();
410
+ for (a, v) in &p.wraps {
411
+ plan::recordWrap(*a, v.clone());
412
+ }
413
+ for (a, v) in &p.patterns {
414
+ plan::recordPattern(*a, v.clone());
415
+ }
416
+ let mut ns: Vec<_> = p.narrowings.iter().collect();
417
+ ns.sort_by_key(|(_, (_, _, fresh))| {
418
+ fresh.rsplit("__n").next().and_then(|k| k.parse::<usize>().ok()).unwrap_or(0)
419
+ });
420
+ for (a, (subject, variant, _)) in ns {
421
+ plan::recordNarrow(*a, subject.clone(), variant.clone());
422
+ }
423
+ for (a, vs) in &p.dispatches {
424
+ plan::recordDispatch(*a, vs.clone());
425
+ }
426
+ }