1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::engine::fold::{self, expr_let::fold_expr_let};
use toasty_core::stmt::{self, Expr, ExprLet, MatchArm, Value};
// --- fold_expr_let unit tests ---
#[test]
fn single_binding_inlined() {
// Let { bindings: [I64(42)], body: Arg(0) } → I64(42)
let mut expr_let = ExprLet {
bindings: vec![Expr::from(42i64)],
body: Box::new(Expr::arg(0)),
};
let result = fold_expr_let(&mut expr_let);
assert_eq!(result, Some(Expr::from(42i64)));
}
#[test]
fn multiple_bindings_inlined() {
// Let { bindings: [I64(1), I64(2)], body: Record([Arg(1), Arg(0)]) }
// → Record([I64(2), I64(1)])
let mut expr_let = ExprLet {
bindings: vec![Expr::from(1i64), Expr::from(2i64)],
body: Box::new(Expr::record([Expr::arg(1), Expr::arg(0)])),
};
let result = fold_expr_let(&mut expr_let);
assert_eq!(
result,
Some(Expr::record([Expr::from(2i64), Expr::from(1i64)]))
);
}
#[test]
fn unstable_binding_not_inlined() {
// Binding contains Default (unstable) → no inlining
let mut expr_let = ExprLet {
bindings: vec![Expr::Default],
body: Box::new(Expr::arg(0)),
};
let result = fold_expr_let(&mut expr_let);
assert!(result.is_none());
}
#[test]
fn body_with_match_inlined() {
// The real-world pattern: nullable relation projection.
// Let { bindings: [I64(5)], body: Match(Arg(0), [Null → Null], Arg(0)) }
// → Match(I64(5), [Null → Null], I64(5))
let mut expr_let = ExprLet {
bindings: vec![Expr::from(5i64)],
body: Box::new(Expr::match_expr(
Expr::arg(0),
vec![MatchArm {
pattern: Value::Null,
expr: Expr::null(),
}],
Expr::arg(0),
)),
};
let result = fold_expr_let(&mut expr_let);
let expected = Expr::match_expr(
Expr::from(5i64),
vec![MatchArm {
pattern: Value::Null,
expr: Expr::null(),
}],
Expr::from(5i64),
);
assert_eq!(result, Some(expected));
}
#[test]
fn outer_arg_nesting_decremented() {
// Body references both the Let binding (nesting=0) and an outer scope
// (nesting=1). After inlining, the outer ref should become nesting=0.
// Let { bindings: [I64(1)], body: Record([Arg(pos=0,nest=0), Arg(pos=0,nest=1)]) }
// → Record([I64(1), Arg(pos=0,nest=0)])
let mut expr_let = ExprLet {
bindings: vec![Expr::from(1i64)],
body: Box::new(Expr::record([
Expr::arg(0),
Expr::Arg(stmt::ExprArg {
position: 0,
nesting: 1,
}),
])),
};
let result = fold_expr_let(&mut expr_let);
assert_eq!(result, Some(Expr::record([Expr::from(1i64), Expr::arg(0)])));
}
// --- end-to-end tests through fold_stmt ---
#[test]
fn let_inlined_through_fold_stmt() {
// End-to-end: fold_stmt should inline the Let.
let mut expr = Expr::Let(ExprLet {
bindings: vec![Expr::from(42i64)],
body: Box::new(Expr::arg(0)),
});
fold::fold_stmt(&mut expr);
assert_eq!(expr, Expr::from(42i64));
}
#[test]
fn nested_let_inlined_bottom_up() {
// Inner Let is inlined first (bottom-up), then the outer Let.
// Outer: Let { bindings: [I64(10)], body: Let { bindings: [Arg(0)], body: Arg(0) } }
// After inner inlining: Let { bindings: [I64(10)], body: Arg(0) }
// After outer inlining: I64(10)
let inner = Expr::Let(ExprLet {
bindings: vec![Expr::arg(0)],
body: Box::new(Expr::arg(0)),
});
let mut expr = Expr::Let(ExprLet {
bindings: vec![Expr::from(10i64)],
body: Box::new(inner),
});
fold::fold_stmt(&mut expr);
assert_eq!(expr, Expr::from(10i64));
}
#[test]
fn let_with_match_fully_simplified() {
// Let { bindings: [Null], body: Match(Arg(0), [Null→Null], Arg(0)) }
// Step 1 (Let inlining): Match(Null, [Null→Null], Null)
// Step 2 (Match folding): Null
//
// The fold visitor recurses on the replacement after a rule fires
// (`fold.rs::visit_expr_mut`), so the new Match — now with a constant
// subject — gets a second pass that folds it to Null.
let mut expr = Expr::Let(ExprLet {
bindings: vec![Expr::null()],
body: Box::new(Expr::match_expr(
Expr::arg(0),
vec![MatchArm {
pattern: Value::Null,
expr: Expr::null(),
}],
Expr::arg(0),
)),
});
fold::fold_stmt(&mut expr);
assert_eq!(expr, Expr::null());
}