diff --git a/CLAUDE.md b/CLAUDE.md index 0fd4b7f..b4c6a4a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,6 +32,7 @@ ## Rust * Performance: Bevorzuge `Rc` + lokales `downcast_ref` gegenüber Deep Copies via `Rc::new(obj.clone())`. +* CRITICAL: Keine Wildcards (`_ =>`, `other => other`) in `match`-Ausdrücken auf `NodeKind` oder `SyntaxKind`. Alle Varianten müssen explizit aufgeführt werden. Exhaustive Matches erzwingen, dass neue Varianten an jeder Stelle bewusst behandelt werden. ## Interaktion diff --git a/docs/BUG_program_node_typeinference.md b/docs/BUG_program_node_typeinference.md deleted file mode 100644 index f1cc1a4..0000000 --- a/docs/BUG_program_node_typeinference.md +++ /dev/null @@ -1,52 +0,0 @@ -# Bug: Program-Node bricht Destructuring-Typinferenz - -## Reproduktion - -Ausgehend vom aktuellen Stand (Lambda-Wrapping in `compile_pipeline`, `parse_expression` in `compile`): - -### Schritt 1: In `compile()` `parse_expression` durch `parse_program` ersetzen - -In `src/ast/environment.rs`, Methode `compile()`: - -```rust -// VORHER (funktioniert): -let syntax_ast = parser.parse_expression(); - -// NACHHER (Typinferenz bricht): -let syntax_ast = parser.parse_program(); -``` - -Außerdem die `at_eof`-Prüfung entfernen (weil `parse_program` alles konsumiert). - -### Schritt 2: Testen - -```bash -cargo run --release --bin ast -- -d -e "((fn [[x y]] (+ x y)) [10 20])" -``` - -**Erwartet:** `Constant: 30` im Dump (Optimizer faltet den Ausdruck) - -**Tatsächlich:** Kein Folding. Die Lambda-Parameter `x` und `y` haben Typ `Any` statt `Int`. HM step 10 (Unifikation) wird übersprungen weil `has_typevar_component` false ist. - -### Schritt 3: Zusätzlich Lambda-Wrapping entfernen (verschärft das Problem) - -In `compile_pipeline()` die Zeile `let wrapped = self.wrap_as_lambda(bound);` entfernen. - -Dann bekommt `check_node_as_bound` einen `Program`-Node statt eines `Lambda`-Nodes. Ergebnis ist dasselbe: `Any`-Parameter, kein Folding. - -## Ursache (unvollständig analysiert) - -Der AST-Unterschied: - -- **Funktioniert:** `Lambda(Call(Lambda([[x y]], body), [10 20]))` — kein Program-Node -- **Bricht:** `Lambda(Program(Call(Lambda([[x y]], body), [10 20])))` — Program-Node dazwischen - -Der TypeChecker erzeugt TypeVars (`?0`, `?1`) für die innere Lambda-Parameter. Aber in `check_params_tuple` (check.rs, Zeile ~241) wird `TypeVar` im Match auf `_ => StaticType::Any` aufgelöst statt propagiert. Dadurch enthält die Signatur `fn([[any any]]) -> int` statt `fn([[?0 ?1]]) -> int`, und HM step 10 überspringt die Unifikation. - -Warum das nur mit Program-Node passiert und nicht ohne, ist unklar. Der `check_params_tuple`-Code hat sich nicht geändert. - -## Betroffener Test - -``` -tests/destructuring.rs::test_nested_destructuring_optimization -``` diff --git a/src/ast/compiler/analyzer.rs b/src/ast/compiler/analyzer.rs index 1f6f1ba..bc20c7b 100644 --- a/src/ast/compiler/analyzer.rs +++ b/src/ast/compiler/analyzer.rs @@ -1,6 +1,6 @@ use crate::ast::nodes::{ Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node, - NodeKind, NodeMetrics, TypedNode, TypedPhase, + NodeKind, NodeMetrics, TypedNode, }; use crate::ast::types::{Identity, Purity, Value}; use std::collections::{HashMap, HashSet}; @@ -58,10 +58,56 @@ impl<'a> Analyzer<'a> { self.collect_globals(e); } } - _ => { - node.kind - .for_each_child(|child| self.collect_globals(child)); + NodeKind::If { cond, then_br, else_br } => { + self.collect_globals(cond); + self.collect_globals(then_br); + if let Some(e) = else_br { + self.collect_globals(e); + } } + NodeKind::Lambda { params, body, .. } => { + self.collect_globals(params); + self.collect_globals(body); + } + NodeKind::Call { callee, args } => { + self.collect_globals(callee); + self.collect_globals(args); + } + NodeKind::Again { args } => { + self.collect_globals(args); + } + NodeKind::Assign { target, value, .. } => { + self.collect_globals(target); + self.collect_globals(value); + } + NodeKind::Tuple { elements } => { + for e in elements { + self.collect_globals(e); + } + } + NodeKind::Record { fields, .. } => { + for (k, v) in fields { + self.collect_globals(k); + self.collect_globals(v); + } + } + NodeKind::GetField { rec, .. } => { + self.collect_globals(rec); + } + NodeKind::Expansion { expanded, .. } => { + self.collect_globals(expanded); + } + // Leaf nodes — no children. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::Identifier { .. } + | NodeKind::FieldAccessor(_) + | NodeKind::Error + | NodeKind::Extension(_) + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } @@ -323,81 +369,3 @@ impl<'a> Analyzer<'a> { } } -trait NodeExt { - fn for_each_child(&self, f: F); -} - -impl NodeExt for NodeKind { - fn for_each_child(&self, mut f: F) { - match self { - NodeKind::If { - cond, - then_br, - else_br, - } => { - f(cond); - f(then_br); - if let Some(e) = else_br { - f(e); - } - } - NodeKind::Def { pattern, value, .. } => { - f(pattern); - f(value); - } - NodeKind::Assign { target, value, .. } => { - f(target); - f(value); - } - NodeKind::GetField { rec, .. } => { - f(rec); - } - NodeKind::Lambda { params, body, .. } => { - f(params); - f(body); - } - NodeKind::Call { callee, args } => { - f(callee); - f(args); - } - NodeKind::Block { exprs } | NodeKind::Program { exprs } => { - for e in exprs { - f(e); - } - } - NodeKind::Tuple { elements } => { - for e in elements { - f(e); - } - } - NodeKind::Record { fields, .. } => { - for (key, val) in fields { - f(key); - f(val); - } - } - NodeKind::Expansion { expanded, .. } => { - f(expanded); - } - NodeKind::Again { args } => { - f(args); - } - NodeKind::MacroDecl { params, body, .. } => { - f(params); - f(body); - } - NodeKind::Template(inner) - | NodeKind::Placeholder(inner) - | NodeKind::Splice(inner) => { - f(inner); - } - // Leaf nodes — no children to visit. - NodeKind::Nop - | NodeKind::Constant(_) - | NodeKind::Identifier { .. } - | NodeKind::FieldAccessor(_) - | NodeKind::Error - | NodeKind::Extension(_) => {} - } - } -} diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 515a442..476bbb6 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -663,7 +663,27 @@ impl Binder { }, ) } - _ => { + // All other syntax kinds are invalid in a binding pattern. + SyntaxKind::Nop + | SyntaxKind::Constant(_) + | SyntaxKind::FieldAccessor(_) + | SyntaxKind::Def { .. } + | SyntaxKind::Assign { .. } + | SyntaxKind::Lambda { .. } + | SyntaxKind::Call { .. } + | SyntaxKind::Again { .. } + | SyntaxKind::If { .. } + | SyntaxKind::Block { .. } + | SyntaxKind::Program { .. } + | SyntaxKind::Record { .. } + | SyntaxKind::GetField { .. } + | SyntaxKind::Expansion { .. } + | SyntaxKind::Extension(_) + | SyntaxKind::Error + | SyntaxKind::MacroDecl { .. } + | SyntaxKind::Template(_) + | SyntaxKind::Placeholder(_) + | SyntaxKind::Splice(_) => { diag.push_error( format!("Invalid node in pattern: {:?}", node.kind), Some(node.identity.clone()), @@ -704,7 +724,27 @@ impl Binder { }, ) } - _ => { + // All other syntax kinds are invalid in an assignment pattern. + SyntaxKind::Nop + | SyntaxKind::Constant(_) + | SyntaxKind::FieldAccessor(_) + | SyntaxKind::Def { .. } + | SyntaxKind::Assign { .. } + | SyntaxKind::Lambda { .. } + | SyntaxKind::Call { .. } + | SyntaxKind::Again { .. } + | SyntaxKind::If { .. } + | SyntaxKind::Block { .. } + | SyntaxKind::Program { .. } + | SyntaxKind::Record { .. } + | SyntaxKind::GetField { .. } + | SyntaxKind::Expansion { .. } + | SyntaxKind::Extension(_) + | SyntaxKind::Error + | SyntaxKind::MacroDecl { .. } + | SyntaxKind::Template(_) + | SyntaxKind::Placeholder(_) + | SyntaxKind::Splice(_) => { diag.push_error( format!("Invalid node in assignment pattern: {:?}", node.kind), Some(node.identity.clone()), diff --git a/src/ast/compiler/captures.rs b/src/ast/compiler/captures.rs index f3cdcaa..52ef7f5 100644 --- a/src/ast/compiler/captures.rs +++ b/src/ast/compiler/captures.rs @@ -108,7 +108,19 @@ impl CapturePass { field, }, - other => other, + // Leaf nodes — no children to recurse into. + kind @ (NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::Identifier { .. } + | NodeKind::FieldAccessor(_) + | NodeKind::Error + | NodeKind::Extension(_)) => kind, + + // Syntax-only variants should not appear after macro expansion. + kind @ (NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_)) => kind, }; node } diff --git a/src/ast/compiler/lowering.rs b/src/ast/compiler/lowering.rs index 59b16fa..6f8321d 100644 --- a/src/ast/compiler/lowering.rs +++ b/src/ast/compiler/lowering.rs @@ -129,9 +129,14 @@ impl Lowering { .map(|e| Rc::new(Self::transform(e.clone(), is_tail_position, allocator))), }, - NodeKind::Block { exprs } => { + NodeKind::Block { exprs } | NodeKind::Program { exprs } => { + let is_program = matches!(&node.kind, NodeKind::Program { .. }); if exprs.is_empty() { - NodeKind::Block { exprs: vec![] } + if is_program { + NodeKind::Program { exprs: vec![] } + } else { + NodeKind::Block { exprs: vec![] } + } } else { // Collect VirtualIds of non-captured locals before // transforming. After all exprs are lowered their @@ -157,33 +162,11 @@ impl Lowering { allocator.free_slot(vid); } - NodeKind::Block { exprs: new_exprs } - } - } - - NodeKind::Program { exprs } => { - if exprs.is_empty() { - NodeKind::Program { exprs: vec![] } - } else { - let scope_locals = Self::collect_scope_locals(exprs); - - let last_idx = exprs.len() - 1; - let mut new_exprs = Vec::with_capacity(exprs.len()); - - for (i, expr) in exprs.iter().enumerate() { - let is_last = i == last_idx; - new_exprs.push(Rc::new(Self::transform( - expr.clone(), - is_tail_position && is_last, - allocator, - ))); + if is_program { + NodeKind::Program { exprs: new_exprs } + } else { + NodeKind::Block { exprs: new_exprs } } - - for vid in scope_locals { - allocator.free_slot(vid); - } - - NodeKind::Program { exprs: new_exprs } } } @@ -349,12 +332,35 @@ impl Lowering { binding: IdentifierBinding::Declaration { addr: Address::Local(vid), .. }, .. } => out.push(*vid), + // Non-local declarations (Global, Upvalue) are not stack-allocated. + NodeKind::Identifier { .. } => {} NodeKind::Tuple { elements } => { for e in elements { Self::collect_pattern_vids(e, out); } } - _ => {} + // Patterns should only contain Identifier and Tuple nodes. + // Other variants indicate a compiler bug upstream. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } } diff --git a/src/ast/compiler/optimizer/engine.rs b/src/ast/compiler/optimizer/engine.rs index 7079a61..62740ed 100644 --- a/src/ast/compiler/optimizer/engine.rs +++ b/src/ast/compiler/optimizer/engine.rs @@ -578,7 +578,27 @@ impl Optimizer { false } } - _ => e.ty.purity >= Purity::SideEffectFree, + // All other expression kinds: removable if side-effect-free. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::Identifier { .. } + | NodeKind::FieldAccessor(_) + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Tuple { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => e.ty.purity >= Purity::SideEffectFree, }; if removable { @@ -821,7 +841,17 @@ impl Optimizer { refreshed_metrics(&node.ty, last_ty), ) } - k => (k.clone(), node.ty.clone()), + // Leaf nodes — no children to recurse into. + NodeKind::Nop => (NodeKind::Nop, node.ty.clone()), + NodeKind::Constant(v) => (NodeKind::Constant(v.clone()), node.ty.clone()), + NodeKind::Error => (NodeKind::Error, node.ty.clone()), + NodeKind::Extension(ext) => (NodeKind::Extension(ext.clone_box()), node.ty.clone()), + + // Syntax-only variants should not appear in AnalyzedPhase. + NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => (NodeKind::Nop, node.ty.clone()), }; Rc::new(Node { @@ -881,7 +911,28 @@ impl Optimizer { elements: new_elements, } } - _ => return node_rc.clone(), + // Patterns only contain Identifier and Tuple nodes. + // All other variants pass through unchanged. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => return node_rc.clone(), }; Rc::new(Node { comments: node.comments.clone(), @@ -899,7 +950,27 @@ impl Optimizer { } } NodeKind::Nop => {} - _ => into.push(node), + // All other variants are individual argument values. + NodeKind::Constant(_) + | NodeKind::Identifier { .. } + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => into.push(node), } } } diff --git a/src/ast/compiler/optimizer/folder.rs b/src/ast/compiler/optimizer/folder.rs index 5f632a7..75d3f73 100644 --- a/src/ast/compiler/optimizer/folder.rs +++ b/src/ast/compiler/optimizer/folder.rs @@ -105,7 +105,28 @@ impl<'a> Folder<'a> { .. } => self.globals.as_ref()?.get(idx.0 as usize)?, NodeKind::Constant(val) => val.clone(), - _ => return None, + // Only global identifiers and constants can be folded at compile time. + NodeKind::Identifier { .. } + | NodeKind::Nop + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Tuple { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => return None, }; let result = match func_val { Value::Function(f) => (f.func)(&arg_values), diff --git a/src/ast/compiler/optimizer/inliner.rs b/src/ast/compiler/optimizer/inliner.rs index e22539e..c724751 100644 --- a/src/ast/compiler/optimizer/inliner.rs +++ b/src/ast/compiler/optimizer/inliner.rs @@ -187,7 +187,28 @@ impl<'a> Inliner<'a> { self.map_params_to_args(el, args, offset, sub, body_usage); } } - _ => {} + // Parameters should only contain Def, Identifier (Declaration), and Tuple. + // Reference identifiers don't appear in parameter patterns. + NodeKind::Identifier { .. } + | NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } @@ -213,7 +234,28 @@ impl<'a> Inliner<'a> { self.collect_parameter_slots_set(el, slots); } } - _ => {} + // Parameters should only contain Def, Identifier (Declaration), and Tuple. + // Reference identifiers don't appear in parameter patterns. + NodeKind::Identifier { .. } + | NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } } diff --git a/src/ast/compiler/optimizer/utils.rs b/src/ast/compiler/optimizer/utils.rs index dcd9980..9d10baf 100644 --- a/src/ast/compiler/optimizer/utils.rs +++ b/src/ast/compiler/optimizer/utils.rs @@ -176,6 +176,7 @@ impl UsageInfo { binding: IdentifierBinding::Declaration { .. }, .. } => {} + NodeKind::Identifier { .. } => {} NodeKind::Def { .. } => {} NodeKind::Assign { info, .. } => { if let Some(addr) = info.addr { @@ -187,7 +188,25 @@ impl UsageInfo { self.collect_pattern(el); } } - _ => {} + // Patterns should only contain Identifier, Def, Assign, and Tuple. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } @@ -205,7 +224,27 @@ impl UsageInfo { self.collect_assigned_from_target(el); } } - _ => {} + // Assignment targets should only contain Identifier and Tuple. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } } @@ -223,6 +262,8 @@ pub fn collect_pattern_addrs(pattern: &AnalyzedNode, out: &mut Vec { out.push(*addr); } + // Non-declaration identifiers (references) don't bind new slots. + NodeKind::Identifier { .. } => {} NodeKind::Def { pattern: inner, .. } => { collect_pattern_addrs(inner, out); } @@ -231,6 +272,25 @@ pub fn collect_pattern_addrs(pattern: &AnalyzedNode, out: &mut Vec {} + // Patterns should only contain Identifier, Def, and Tuple. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => {} } } diff --git a/src/ast/compiler/type_checker/check.rs b/src/ast/compiler/type_checker/check.rs index a8bdbfa..90498a5 100644 --- a/src/ast/compiler/type_checker/check.rs +++ b/src/ast/compiler/type_checker/check.rs @@ -172,7 +172,25 @@ impl TypeChecker { } NodeKind::Nop => (NodeKind::Nop, StaticType::Void), NodeKind::Error => (NodeKind::Error, StaticType::Error), - _ => { + // All remaining variants are invalid in a parameter list. + // Identifier::Reference should not appear in parameter patterns. + NodeKind::Identifier { .. } + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::GetField { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => { diag.push_error( "Invalid node in parameter list", Some(node.identity.clone()), @@ -328,7 +346,29 @@ impl TypeChecker { } }, }, - _ => NodeKind::Error, + // Tuple patterns are handled above with an early return. + // All other variants are invalid as def patterns. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Tuple { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => NodeKind::Error, }, ty: ty.clone(), comments: pattern.comments.clone(), @@ -485,7 +525,8 @@ impl TypeChecker { ) } - NodeKind::Block { exprs } => { + NodeKind::Block { exprs } | NodeKind::Program { exprs } => { + let is_program = matches!(&node.kind, NodeKind::Program { .. }); let mut typed_exprs = Vec::new(); let mut last_ty = StaticType::Void; @@ -495,19 +536,12 @@ impl TypeChecker { typed_exprs.push(Rc::new(t)); } - (NodeKind::Block { exprs: typed_exprs }, last_ty) - } - NodeKind::Program { exprs } => { - let mut typed_exprs = Vec::new(); - let mut last_ty = StaticType::Void; - - for e in exprs { - let t = self.check_node(e, ctx, diag); - last_ty = t.ty.clone(); - typed_exprs.push(Rc::new(t)); - } - - (NodeKind::Program { exprs: typed_exprs }, last_ty) + let kind = if is_program { + NodeKind::Program { exprs: typed_exprs } + } else { + NodeKind::Block { exprs: typed_exprs } + }; + (kind, last_ty) } NodeKind::Lambda { diff --git a/src/ast/vm.rs b/src/ast/vm.rs index af782d9..9ff1e8a 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -62,17 +62,14 @@ impl VMObserver for TracingObserver { fn after_eval(&mut self, vm: &VM, node: &ExecNode, res: &Result) { self.indent = self.indent.saturating_sub(1); let pad = self.pad(); - match &node.kind { - NodeKind::Def { .. } | NodeKind::Assign { .. } => { - let s_pad = format!("{}| ", pad); - self.logs.push(format!("{}--- Scope Status ---", s_pad)); - self.logs.push(format!( - "{}Stack (top 5): {:?}", - s_pad, - vm.stack.iter().rev().take(5).collect::>() - )); - } - _ => {} + if matches!(&node.kind, NodeKind::Def { .. } | NodeKind::Assign { .. }) { + let s_pad = format!("{}| ", pad); + self.logs.push(format!("{}--- Scope Status ---", s_pad)); + self.logs.push(format!( + "{}Stack (top 5): {:?}", + s_pad, + vm.stack.iter().rev().take(5).collect::>() + )); } let res_str = match res { Ok(v) => format!("{}", v), @@ -864,7 +861,26 @@ impl VM { } Ok(()) } - _ => { + // All other variants: evaluate and push result(s) onto stack. + NodeKind::Nop + | NodeKind::Identifier { .. } + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => { let val = self.eval_internal(obs, args)?; if let Some(slice) = val.as_slice() { self.stack.extend_from_slice(slice); @@ -1014,7 +1030,27 @@ impl VM { } Ok(()) } - _ => Err("Invalid node in parameter pattern".to_string()), + // Parameter patterns should only contain Identifier and Tuple. + NodeKind::Nop + | NodeKind::Constant(_) + | NodeKind::FieldAccessor(_) + | NodeKind::Def { .. } + | NodeKind::Assign { .. } + | NodeKind::Lambda { .. } + | NodeKind::Call { .. } + | NodeKind::Again { .. } + | NodeKind::If { .. } + | NodeKind::Block { .. } + | NodeKind::Program { .. } + | NodeKind::Record { .. } + | NodeKind::GetField { .. } + | NodeKind::Expansion { .. } + | NodeKind::Extension(_) + | NodeKind::Error + | NodeKind::MacroDecl { .. } + | NodeKind::Template(_) + | NodeKind::Placeholder(_) + | NodeKind::Splice(_) => Err("Invalid node in parameter pattern".to_string()), } } }