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:
@@ -32,6 +32,7 @@
|
|||||||
## Rust
|
## Rust
|
||||||
|
|
||||||
* Performance: Bevorzuge `Rc<dyn Trait>` + lokales `downcast_ref` gegenüber Deep Copies via `Rc::new(obj.clone())`.
|
* Performance: Bevorzuge `Rc<dyn Trait>` + 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
|
## Interaktion
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
```
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::ast::nodes::{
|
use crate::ast::nodes::{
|
||||||
Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node,
|
Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node,
|
||||||
NodeKind, NodeMetrics, TypedNode, TypedPhase,
|
NodeKind, NodeMetrics, TypedNode,
|
||||||
};
|
};
|
||||||
use crate::ast::types::{Identity, Purity, Value};
|
use crate::ast::types::{Identity, Purity, Value};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@@ -58,10 +58,56 @@ impl<'a> Analyzer<'a> {
|
|||||||
self.collect_globals(e);
|
self.collect_globals(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
NodeKind::If { cond, then_br, else_br } => {
|
||||||
node.kind
|
self.collect_globals(cond);
|
||||||
.for_each_child(|child| self.collect_globals(child));
|
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(
|
diag.push_error(
|
||||||
format!("Invalid node in pattern: {:?}", node.kind),
|
format!("Invalid node in pattern: {:?}", node.kind),
|
||||||
Some(node.identity.clone()),
|
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(
|
diag.push_error(
|
||||||
format!("Invalid node in assignment pattern: {:?}", node.kind),
|
format!("Invalid node in assignment pattern: {:?}", node.kind),
|
||||||
Some(node.identity.clone()),
|
Some(node.identity.clone()),
|
||||||
|
|||||||
@@ -108,7 +108,19 @@ impl CapturePass {
|
|||||||
field,
|
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
|
node
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,9 +129,14 @@ impl Lowering {
|
|||||||
.map(|e| Rc::new(Self::transform(e.clone(), is_tail_position, allocator))),
|
.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() {
|
if exprs.is_empty() {
|
||||||
NodeKind::Block { exprs: vec![] }
|
if is_program {
|
||||||
|
NodeKind::Program { exprs: vec![] }
|
||||||
|
} else {
|
||||||
|
NodeKind::Block { exprs: vec![] }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Collect VirtualIds of non-captured locals before
|
// Collect VirtualIds of non-captured locals before
|
||||||
// transforming. After all exprs are lowered their
|
// transforming. After all exprs are lowered their
|
||||||
@@ -157,33 +162,11 @@ impl Lowering {
|
|||||||
allocator.free_slot(vid);
|
allocator.free_slot(vid);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeKind::Block { exprs: new_exprs }
|
if is_program {
|
||||||
}
|
NodeKind::Program { exprs: new_exprs }
|
||||||
}
|
} else {
|
||||||
|
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,
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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), .. },
|
binding: IdentifierBinding::Declaration { addr: Address::Local(vid), .. },
|
||||||
..
|
..
|
||||||
} => out.push(*vid),
|
} => out.push(*vid),
|
||||||
|
// Non-local declarations (Global, Upvalue) are not stack-allocated.
|
||||||
|
NodeKind::Identifier { .. } => {}
|
||||||
NodeKind::Tuple { elements } => {
|
NodeKind::Tuple { elements } => {
|
||||||
for e in elements {
|
for e in elements {
|
||||||
Self::collect_pattern_vids(e, out);
|
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
|
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 {
|
if removable {
|
||||||
@@ -821,7 +841,17 @@ impl Optimizer {
|
|||||||
refreshed_metrics(&node.ty, last_ty),
|
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 {
|
Rc::new(Node {
|
||||||
@@ -881,7 +911,28 @@ impl Optimizer {
|
|||||||
elements: new_elements,
|
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 {
|
Rc::new(Node {
|
||||||
comments: node.comments.clone(),
|
comments: node.comments.clone(),
|
||||||
@@ -899,7 +950,27 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeKind::Nop => {}
|
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)?,
|
} => self.globals.as_ref()?.get(idx.0 as usize)?,
|
||||||
NodeKind::Constant(val) => val.clone(),
|
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 {
|
let result = match func_val {
|
||||||
Value::Function(f) => (f.func)(&arg_values),
|
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);
|
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);
|
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 { .. },
|
binding: IdentifierBinding::Declaration { .. },
|
||||||
..
|
..
|
||||||
} => {}
|
} => {}
|
||||||
|
NodeKind::Identifier { .. } => {}
|
||||||
NodeKind::Def { .. } => {}
|
NodeKind::Def { .. } => {}
|
||||||
NodeKind::Assign { info, .. } => {
|
NodeKind::Assign { info, .. } => {
|
||||||
if let Some(addr) = info.addr {
|
if let Some(addr) = info.addr {
|
||||||
@@ -187,7 +188,25 @@ impl UsageInfo {
|
|||||||
self.collect_pattern(el);
|
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);
|
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);
|
out.push(*addr);
|
||||||
}
|
}
|
||||||
|
// Non-declaration identifiers (references) don't bind new slots.
|
||||||
|
NodeKind::Identifier { .. } => {}
|
||||||
NodeKind::Def { pattern: inner, .. } => {
|
NodeKind::Def { pattern: inner, .. } => {
|
||||||
collect_pattern_addrs(inner, out);
|
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);
|
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::Nop => (NodeKind::Nop, StaticType::Void),
|
||||||
NodeKind::Error => (NodeKind::Error, StaticType::Error),
|
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(
|
diag.push_error(
|
||||||
"Invalid node in parameter list",
|
"Invalid node in parameter list",
|
||||||
Some(node.identity.clone()),
|
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(),
|
ty: ty.clone(),
|
||||||
comments: pattern.comments.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 typed_exprs = Vec::new();
|
||||||
let mut last_ty = StaticType::Void;
|
let mut last_ty = StaticType::Void;
|
||||||
|
|
||||||
@@ -495,19 +536,12 @@ impl TypeChecker {
|
|||||||
typed_exprs.push(Rc::new(t));
|
typed_exprs.push(Rc::new(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
(NodeKind::Block { exprs: typed_exprs }, last_ty)
|
let kind = if is_program {
|
||||||
}
|
NodeKind::Program { exprs: typed_exprs }
|
||||||
NodeKind::Program { exprs } => {
|
} else {
|
||||||
let mut typed_exprs = Vec::new();
|
NodeKind::Block { exprs: typed_exprs }
|
||||||
let mut last_ty = StaticType::Void;
|
};
|
||||||
|
(kind, last_ty)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeKind::Lambda {
|
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>) {
|
fn after_eval(&mut self, vm: &VM, node: &ExecNode, res: &Result<Value, String>) {
|
||||||
self.indent = self.indent.saturating_sub(1);
|
self.indent = self.indent.saturating_sub(1);
|
||||||
let pad = self.pad();
|
let pad = self.pad();
|
||||||
match &node.kind {
|
if matches!(&node.kind, NodeKind::Def { .. } | NodeKind::Assign { .. }) {
|
||||||
NodeKind::Def { .. } | NodeKind::Assign { .. } => {
|
let s_pad = format!("{}| ", pad);
|
||||||
let s_pad = format!("{}| ", pad);
|
self.logs.push(format!("{}--- Scope Status ---", s_pad));
|
||||||
self.logs.push(format!("{}--- Scope Status ---", s_pad));
|
self.logs.push(format!(
|
||||||
self.logs.push(format!(
|
"{}Stack (top 5): {:?}",
|
||||||
"{}Stack (top 5): {:?}",
|
s_pad,
|
||||||
s_pad,
|
vm.stack.iter().rev().take(5).collect::<Vec<_>>()
|
||||||
vm.stack.iter().rev().take(5).collect::<Vec<_>>()
|
));
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
let res_str = match res {
|
let res_str = match res {
|
||||||
Ok(v) => format!("{}", v),
|
Ok(v) => format!("{}", v),
|
||||||
@@ -864,7 +861,26 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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)?;
|
let val = self.eval_internal(obs, args)?;
|
||||||
if let Some(slice) = val.as_slice() {
|
if let Some(slice) = val.as_slice() {
|
||||||
self.stack.extend_from_slice(slice);
|
self.stack.extend_from_slice(slice);
|
||||||
@@ -1014,7 +1030,27 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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