Enforce exhaustive matches for AST nodes
This commit addresses the critical rule regarding exhaustive `match` expressions on AST node kinds. By explicitly listing all variants instead of using wildcards (`_ =>`), we ensure that new AST node types are consciously handled throughout the compilation pipeline. This practice prevents subtle bugs and makes the compiler more robust against future changes. The `CLAUDE.md` file has been updated to reflect this critical rule. The deleted `BUG_program_node_typeinference.md` file indicates that a previously identified bug related to type inference with `Program` nodes has been resolved, likely as a consequence of enforcing these exhaustive matches.
This commit is contained in:
@@ -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<F: FnMut(&TypedNode)>(&self, f: F);
|
||||
}
|
||||
|
||||
impl NodeExt for NodeKind<TypedPhase> {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Address<Virtu
|
||||
} => {
|
||||
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<Address<Virtu
|
||||
collect_pattern_addrs(el, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
// 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(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+49
-13
@@ -62,17 +62,14 @@ impl VMObserver for TracingObserver {
|
||||
fn after_eval(&mut self, vm: &VM, node: &ExecNode, res: &Result<Value, String>) {
|
||||
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::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
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::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user