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