selfhost: mirror defer; e2e tests for LIFO ordering
C cgen has carried defer for a while (defers[] global + reverse walk on every return). Selfhost cgen now mirrors: - cgen struct: deferbuf (**node, LIFO stack) + defertop counter. - cgstmt N_DEFER: push n.lhs. - cgreturn: rundefers() at entry — same as the C cgen pattern. - cgfn fall-through return: rundefers() before zero-AX+RET. DEFER_MAX = 16 matches C cgen. Two new e2e rows: defer with an explicit `return acc;` (321 mod 256 = 65), and defer firing on an implicit void-fn fall-through (87). Both rows verified via the wwstage cgen too. Defer's semantics: queued exprs fire LIFO before the return expr is evaluated, so a return that reads memory mutated by a deferred call sees the post-defer state. Matches C cgen and Hare.
This commit is contained in:
@@ -538,6 +538,35 @@ static const struct row rows[] = {
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 3 },
|
||||
/* defer runs queued exprs in LIFO order, before the return
|
||||
* expression is evaluated. Each call appends a decimal digit
|
||||
* to acc via *&acc — return reads the post-defer state. */
|
||||
{ "fn rec(p: *i32, c: i32) i32 = {\n"
|
||||
" *p = *p * 10 + c;\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let acc: i32 = 0;\n"
|
||||
" defer rec(&acc, 1);\n"
|
||||
" defer rec(&acc, 2);\n"
|
||||
" defer rec(&acc, 3);\n"
|
||||
" return acc;\n"
|
||||
"};", 65 }, /* 321 mod 256 */
|
||||
/* defer also fires on an implicit fall-through return (void fn). */
|
||||
{ "fn rec(p: *i32, c: i32) i32 = {\n"
|
||||
" *p = *p * 10 + c;\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"fn run(p: *i32) void = {\n"
|
||||
" defer rec(p, 7);\n"
|
||||
" defer rec(p, 8);\n"
|
||||
" // no explicit return — implicit fall-through path\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let acc: i32 = 0;\n"
|
||||
" run(&acc);\n"
|
||||
" return acc;\n"
|
||||
"};", 87 }, /* defers fire 8, 7 → 8 then 87 */
|
||||
/* yield from match-as-expression: each arm yields a value;
|
||||
* the match itself is bound to a let. */
|
||||
{ "fn pick(b: bool) (i32 | str) = {\n"
|
||||
|
||||
Reference in New Issue
Block a user