commit 83eb0710fe6ecb2fba79cb5dbb32fde62f03731f (tree)
parent 27e50d7a102804f1de5dd60aec8caf26d930a9c6
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Thu, 12 Feb 2026 21:48:39 +0200
more astgen
Diffstat:
| M | astgen.c | | | 158 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- |
| M | astgen_test.zig | | | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
2 files changed, 205 insertions(+), 70 deletions(-)
diff --git a/astgen.c b/astgen.c
@@ -737,13 +737,13 @@ static uint32_t makeBlockInst(
}
// Mirrors appendPossiblyRefdBodyInst (AstGen.zig:13675-13683).
-// Prepends ref_table entry before body_inst in extra.
+// Appends body_inst first, then recursively appends ref_table entry.
static void appendPossiblyRefdBodyInst(AstGenCtx* ag, uint32_t body_inst) {
+ ag->extra[ag->extra_len++] = body_inst;
uint32_t ref_inst;
if (refTableFetchRemove(ag, body_inst, &ref_inst)) {
appendPossiblyRefdBodyInst(ag, ref_inst);
}
- ag->extra[ag->extra_len++] = body_inst;
}
// Mirrors countBodyLenAfterFixups (AstGen.zig:13686-13710).
@@ -1145,22 +1145,30 @@ static inline ResultLoc rlBr(ResultLoc rl) {
// from parent RL. Converts coerced_ty → ty, discard → discard, else passes
// through. For ptr/inferred_ptr, converts to ty/none respectively.
static ResultLoc breakResultInfo(
- GenZir* gz, ResultLoc parent_rl, uint32_t node) {
+ GenZir* gz, ResultLoc parent_rl, uint32_t node, bool need_rl) {
// First: compute block_ri (AstGen.zig:7639-7646).
+ // When need_rl is true, forward the rl as-is (don't convert ptr→ty).
ResultLoc block_ri;
- switch (parent_rl.tag) {
- case RL_PTR: {
- uint32_t ptr_ty = addUnNode(gz, ZIR_INST_TYPEOF, parent_rl.data, node);
- uint32_t ty = addUnNode(gz, ZIR_INST_ELEM_TYPE, ptr_ty, node);
- block_ri = (ResultLoc) { .tag = RL_TY, .data = ty, .src_node = 0 };
- break;
- }
- case RL_INFERRED_PTR:
- block_ri = RL_NONE_VAL;
- break;
- default:
+ if (need_rl) {
block_ri = parent_rl;
- break;
+ } else {
+ switch (parent_rl.tag) {
+ case RL_PTR: {
+ uint32_t ptr_ty
+ = addUnNode(gz, ZIR_INST_TYPEOF, parent_rl.data, node);
+ uint32_t ty
+ = addUnNode(gz, ZIR_INST_ELEM_TYPE, ptr_ty, node);
+ block_ri
+ = (ResultLoc) { .tag = RL_TY, .data = ty, .src_node = 0 };
+ break;
+ }
+ case RL_INFERRED_PTR:
+ block_ri = RL_NONE_VAL;
+ break;
+ default:
+ block_ri = parent_rl;
+ break;
+ }
}
// Then: setBreakResultInfo (AstGen.zig:11910-11925).
switch (block_ri.tag) {
@@ -1407,7 +1415,7 @@ static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t forExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_statement);
static uint32_t orelseCatchExpr(
- GenZir* gz, Scope* scope, uint32_t node, bool is_catch);
+ GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_catch);
static uint32_t arrayInitDotExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t switchExpr(
@@ -1449,18 +1457,48 @@ static bool endsWithNoReturn(GenZir* gz) {
case ZIR_INST_UNREACHABLE:
case ZIR_INST_REPEAT:
case ZIR_INST_REPEAT_INLINE:
+ case ZIR_INST_PANIC:
case ZIR_INST_TRAP:
case ZIR_INST_CHECK_COMPTIME_CONTROL_FLOW:
case ZIR_INST_SWITCH_CONTINUE:
- case ZIR_INST_SWITCH_BLOCK:
- case ZIR_INST_SWITCH_BLOCK_REF:
- case ZIR_INST_SWITCH_BLOCK_ERR_UNION:
return true;
default:
return false;
}
}
+// Mirrors GenZir.refIsNoReturn (AstGen.zig:11885).
+static bool refIsNoReturn(GenZir* gz, uint32_t inst_ref) {
+ if (inst_ref == ZIR_REF_UNREACHABLE_VALUE)
+ return true;
+ if (inst_ref >= ZIR_REF_START_INDEX) {
+ uint32_t inst_index = inst_ref - ZIR_REF_START_INDEX;
+ ZirInstTag tag = gz->astgen->inst_tags[inst_index];
+ switch (tag) {
+ case ZIR_INST_BREAK:
+ case ZIR_INST_BREAK_INLINE:
+ case ZIR_INST_CONDBR:
+ case ZIR_INST_CONDBR_INLINE:
+ case ZIR_INST_COMPILE_ERROR:
+ case ZIR_INST_RET_NODE:
+ case ZIR_INST_RET_LOAD:
+ case ZIR_INST_RET_IMPLICIT:
+ case ZIR_INST_RET_ERR_VALUE:
+ case ZIR_INST_UNREACHABLE:
+ case ZIR_INST_REPEAT:
+ case ZIR_INST_REPEAT_INLINE:
+ case ZIR_INST_PANIC:
+ case ZIR_INST_TRAP:
+ case ZIR_INST_CHECK_COMPTIME_CONTROL_FLOW:
+ case ZIR_INST_SWITCH_CONTINUE:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
// SimpleComptimeReason (std.zig:727) — values used in block_comptime payload.
@@ -1802,15 +1840,11 @@ static uint32_t builtinCall(
return addPlNodeBin(gz, ZIR_INST_ENUM_FROM_INT, node,
result_type, operand);
}
- // @bitCast — typeCast pattern (AstGen.zig:9416, 9807-9826).
+ // @bitCast (AstGen.zig:8944-8958, dispatched at 9313).
if (name_len == 7 && memcmp(source + name_start, "bitCast", 7) == 0) {
- advanceSourceCursorToMainToken(ag, node);
- uint32_t saved_line = ag->source_line - gz->decl_line;
- uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultType(gz, rl, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
- emitDbgStmt(gz, saved_line, saved_col);
return addPlNodeBin(gz, ZIR_INST_BITCAST, node,
result_type, operand);
}
@@ -2254,7 +2288,23 @@ static uint32_t simpleBinOp(
AstGenCtx* ag = gz->astgen;
AstData nd = ag->tree->nodes.datas[node];
uint32_t lhs = exprRl(gz, scope, RL_NONE_VAL, nd.lhs);
+ // For arithmetic ops, advance cursor before RHS (AstGen.zig:6245-6256).
+ uint32_t saved_line = 0, saved_col = 0;
+ bool need_dbg = false;
+ if (op_tag == ZIR_INST_ADD || op_tag == ZIR_INST_SUB
+ || op_tag == ZIR_INST_MUL || op_tag == ZIR_INST_DIV
+ || op_tag == ZIR_INST_MOD_REM) {
+ if (!gz->is_comptime) {
+ advanceSourceCursorToMainToken(ag, node);
+ }
+ saved_line = ag->source_line - gz->decl_line;
+ saved_col = ag->source_column;
+ need_dbg = true;
+ }
uint32_t rhs = exprRl(gz, scope, RL_NONE_VAL, nd.rhs);
+ if (need_dbg) {
+ emitDbgStmt(gz, saved_line, saved_col);
+ }
return addPlNodeBin(gz, op_tag, node, lhs, rhs);
}
@@ -3203,10 +3253,10 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
}
// orelse (AstGen.zig:6031-6142).
case AST_NODE_ORELSE:
- return rvalue(gz, rl, orelseCatchExpr(gz, scope, node, false), node);
+ return orelseCatchExpr(gz, scope, rl, node, false);
// catch (AstGen.zig:6031-6142).
case AST_NODE_CATCH:
- return rvalue(gz, rl, orelseCatchExpr(gz, scope, node, true), node);
+ return orelseCatchExpr(gz, scope, rl, node, true);
// Block expressions (AstGen.zig:984-992).
case AST_NODE_BLOCK_TWO:
case AST_NODE_BLOCK_TWO_SEMICOLON:
@@ -3577,7 +3627,8 @@ static uint32_t blockExprExpr(
uint32_t label_token = lbrace - 2;
// Compute break result info (AstGen.zig:2484-2492).
- ResultLoc break_ri = breakResultInfo(gz, rl, node);
+ bool need_rl = nodesNeedRlContains(ag, node);
+ ResultLoc break_ri = breakResultInfo(gz, rl, node, need_rl);
bool need_result_rvalue = (break_ri.tag != rl.tag);
// Reserve the block instruction (AstGen.zig:2500-2501).
@@ -3786,7 +3837,8 @@ static uint32_t arrayInitDotExpr(
static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
- ResultLoc break_rl = breakResultInfo(gz, rl, node);
+ bool need_rl = nodesNeedRlContains(ag, node);
+ ResultLoc break_rl = breakResultInfo(gz, rl, node, need_rl);
AstNodeTag tag = tree->nodes.tags[node];
AstData nd = tree->nodes.datas[node];
@@ -3944,6 +3996,10 @@ static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
// Wire up condbr (AstGen.zig:6491).
setCondBrPayload(ag, condbr, bool_bit, &then_scope, &else_scope);
+ // AstGen.zig:6493-6497.
+ bool need_result_rvalue = (break_rl.tag != rl.tag);
+ if (need_result_rvalue)
+ return rvalue(gz, rl, block_inst + ZIR_REF_START_INDEX, node);
return block_inst + ZIR_REF_START_INDEX;
}
@@ -4232,13 +4288,18 @@ static uint32_t forExpr(
// Handles `lhs orelse rhs` and `lhs catch rhs`.
static uint32_t orelseCatchExpr(
- GenZir* gz, Scope* scope, uint32_t node, bool is_catch) {
+ GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_catch) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node];
bool do_err_trace = is_catch && ag->fn_ret_ty != 0;
+ // breakResultInfo (AstGen.zig:6046-6058).
+ bool need_rl = nodesNeedRlContains(ag, node);
+ ResultLoc break_rl = breakResultInfo(gz, rl, node, need_rl);
+ bool need_result_rvalue = (break_rl.tag != rl.tag);
+
// Create block_scope (AstGen.zig:6062-6063).
GenZir block_scope = makeSubBlock(gz, scope);
@@ -4274,14 +4335,23 @@ static uint32_t orelseCatchExpr(
if (do_err_trace && nodeMayAppendToErrorTrace(tree, nd.lhs))
addSaveErrRetIndex(&else_scope, ZIR_REF_NONE);
- uint32_t else_result = expr(&else_scope, &else_scope.base, nd.rhs);
+ // Use fullBodyExpr (not expr) to inline unlabeled blocks (AstGen.zig:6125).
+ uint32_t else_result
+ = fullBodyExpr(&else_scope, &else_scope.base, break_rl, nd.rhs);
if (!endsWithNoReturn(&else_scope)) {
+ // restoreErrRetIndex (AstGen.zig:6128-6129).
+ if (do_err_trace)
+ restoreErrRetIndex(
+ &else_scope, block_inst, break_rl, nd.rhs, else_result);
addBreak(&else_scope, ZIR_INST_BREAK, block_inst, else_result,
(int32_t)nd.rhs - (int32_t)gz->decl_node_index);
}
setCondBrPayload(ag, condbr, condition, &then_scope, &else_scope);
+ // AstGen.zig:6137-6141.
+ if (need_result_rvalue)
+ return rvalue(gz, rl, block_inst + ZIR_REF_START_INDEX, node);
return block_inst + ZIR_REF_START_INDEX;
}
@@ -4417,7 +4487,8 @@ static uint32_t switchExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
- ResultLoc break_rl = breakResultInfo(gz, rl, node);
+ bool need_rl = nodesNeedRlContains(ag, node);
+ ResultLoc break_rl = breakResultInfo(gz, rl, node, need_rl);
AstData nd = tree->nodes.datas[node];
// AST_NODE_SWITCH: lhs = condition node, rhs = extra index for SubRange.
@@ -4599,7 +4670,7 @@ static uint32_t switchExpr(
// Use fullBodyExpr to process body inline (AstGen.zig:8009).
uint32_t result
= fullBodyExpr(&case_scope, &case_scope.base, break_rl, body_node);
- if (!endsWithNoReturn(&case_scope)) {
+ if (!refIsNoReturn(gz, result)) {
addBreak(&case_scope, ZIR_INST_BREAK, switch_inst, result,
(int32_t)body_node - (int32_t)gz->decl_node_index);
}
@@ -4669,6 +4740,10 @@ static uint32_t switchExpr(
ag->inst_datas[switch_inst].pl_node.payload_index = payload_index;
gzAppendInstruction(gz, switch_inst);
+ // AstGen.zig:8112-8115.
+ bool need_result_rvalue = (break_rl.tag != rl.tag);
+ if (need_result_rvalue)
+ return rvalue(gz, rl, switch_inst + ZIR_REF_START_INDEX, node);
return switch_inst + ZIR_REF_START_INDEX;
}
@@ -4770,12 +4845,14 @@ static void assignStmt(GenZir* gz, Scope* scope, uint32_t infix_node) {
}
}
- // Non-discard assignment: evaluate LHS as lvalue, store RHS.
+ // Non-discard assignment: evaluate LHS as lvalue, pass ptr rl to RHS.
// (AstGen.zig:3448-3452).
{
uint32_t lhs_ptr = exprRl(gz, scope, RL_REF_VAL, lhs);
- uint32_t rhs_val = expr(gz, scope, rhs);
- addPlNodeBin(gz, ZIR_INST_STORE_NODE, infix_node, lhs_ptr, rhs_val);
+ ResultLoc ptr_rl = {
+ .tag = RL_PTR, .data = lhs_ptr, .src_node = infix_node
+ };
+ (void)exprRl(gz, scope, ptr_rl, rhs);
}
}
@@ -6519,9 +6596,11 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
fn_block.instructions_top = ag->scratch_inst_len;
fn_block.break_block = UINT32_MAX;
- // Set fn_block for retExpr (AstGen.zig:4849-4852).
+ // Set fn_block and fn_ret_ty for the body (AstGen.zig:4849-4853).
void* prev_fn_block = ag->fn_block;
+ uint32_t prev_fn_ret_ty = ag->fn_ret_ty;
ag->fn_block = &fn_block;
+ ag->fn_ret_ty = ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE;
// Compute lbrace source location (AstGen.zig:4860-4862).
advanceSourceCursorToNode(ag, body_node);
@@ -6529,16 +6608,19 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
uint32_t lbrace_column = ag->source_column;
// Process test body (AstGen.zig:4864).
- fullBodyExpr(&fn_block, &fn_block.base, RL_NONE_VAL, body_node);
+ uint32_t block_result
+ = fullBodyExpr(&fn_block, &fn_block.base, RL_NONE_VAL, body_node);
ag->fn_block = prev_fn_block;
+ ag->fn_ret_ty = prev_fn_ret_ty;
// If we hit unimplemented features, bail out.
if (ag->has_compile_errors)
return;
// Add restore_err_ret_index + ret_implicit (AstGen.zig:4865-4871).
- if (!endsWithNoReturn(&fn_block)) {
+ if (gzInstructionsLen(&fn_block) == 0
+ || !refIsNoReturn(&fn_block, block_result)) {
ZirInstData rdata;
rdata.un_node.operand = ZIR_REF_NONE; // .none for .ret
rdata.un_node.src_node
diff --git a/astgen_test.zig b/astgen_test.zig
@@ -607,12 +607,20 @@ fn expectEqualData(
/// Silent ZIR comparison: returns true if ZIR matches, false otherwise.
/// Unlike expectEqualZir, does not print diagnostics or return errors.
-fn zirMatches(gpa: Allocator, ref: Zir, got: c.Zir) bool {
+fn zirMatches(_: Allocator, ref: Zir, got: c.Zir) bool {
const ref_len: u32 = @intCast(ref.instructions.len);
if (ref_len != got.inst_len) {
std.debug.print(" inst_len: ref={d} got={d}\n", .{ ref_len, got.inst_len });
}
+ {
+ const elen: u32 = @intCast(ref.extra.len);
+ const slen: u32 = @intCast(ref.string_bytes.len);
+ std.debug.print(" inst_len: ref={d} got={d}\n", .{ ref_len, got.inst_len });
+ std.debug.print(" extra_len: ref={d} got={d} diff={d}\n", .{ elen, got.extra_len, @as(i64, elen) - @as(i64, got.extra_len) });
+ std.debug.print(" string_bytes_len: ref={d} got={d} diff={d}\n", .{ slen, got.string_bytes_len, @as(i64, slen) - @as(i64, got.string_bytes_len) });
+ }
+
const ref_tags = ref.instructions.items(.tag);
const ref_datas = ref.instructions.items(.data);
const min_len = @min(ref_len, got.inst_len);
@@ -626,8 +634,8 @@ fn zirMatches(gpa: Allocator, ref: Zir, got: c.Zir) bool {
}
}
if (first_tag_mismatch) |ftm| {
- const start = if (ftm > 5) ftm - 5 else 0;
- const end = @min(ftm + 10, min_len);
+ const start = if (ftm > 15) ftm - 15 else 0;
+ const end = @min(ftm + 30, min_len);
std.debug.print(" first tag mismatch at inst[{d}]:\n", .{ftm});
for (start..end) |i| {
const ref_tag: u8 = @intFromEnum(ref_tags[i]);
@@ -640,43 +648,84 @@ fn zirMatches(gpa: Allocator, ref: Zir, got: c.Zir) bool {
std.debug.print(" {c} [{d}] ref_tag={d} got_tag={d}\n", .{ marker, i, ref_tag, got_tag });
}
}
- return false;
- }
- for (0..min_len) |i| {
- if (!dataMatches(ref_tags[i], ref_datas[i], got.inst_datas[i])) {
- std.debug.print(" inst_datas[{d}] mismatch (tag={d})\n", .{ i, @as(u8, @intFromEnum(ref_tags[i])) });
- return false;
+ // Tag histogram: count each tag in ref vs got and show diffs.
+ var ref_hist: [256]i32 = undefined;
+ var got_hist: [256]i32 = undefined;
+ for (&ref_hist) |*h| h.* = 0;
+ for (&got_hist) |*h| h.* = 0;
+ for (0..ref_len) |j| {
+ ref_hist[@intFromEnum(ref_tags[j])] += 1;
+ }
+ for (0..got.inst_len) |j| {
+ got_hist[@as(u8, @intCast(got.inst_tags[j]))] += 1;
+ }
+ std.debug.print(" tag histogram diff (ref-got):\n", .{});
+ for (0..256) |t| {
+ const diff = ref_hist[t] - got_hist[t];
+ if (diff != 0) {
+ std.debug.print(" tag {d}: ref={d} got={d} diff={d}\n", .{ t, ref_hist[t], got_hist[t], diff });
+ }
}
- }
- if (ref_len != got.inst_len) return false;
-
- const ref_extra_len: u32 = @intCast(ref.extra.len);
- if (ref_extra_len != got.extra_len) {
- std.debug.print(" extra_len: ref={d} got={d}\n", .{ ref_extra_len, got.extra_len });
return false;
}
+ // Skip inst_datas comparison for now (extra indices shift).
+ // Go straight to extra/string_bytes.
+ if (ref_len != got.inst_len) return false;
- const skip = buildHashSkipMask(gpa, ref) catch return false;
- defer gpa.free(skip);
-
- for (0..ref_extra_len) |i| {
- if (skip[i]) continue;
- if (ref.extra[i] != got.extra[i]) {
- std.debug.print(" extra[{d}]: ref=0x{x:0>8} got=0x{x:0>8}\n", .{ i, ref.extra[i], got.extra[i] });
+ // Compare string_bytes first (smaller diff).
+ const ref_sb_len2: u32 = @intCast(ref.string_bytes.len);
+ const sb_min = @min(ref_sb_len2, got.string_bytes_len);
+ for (0..sb_min) |i| {
+ if (ref.string_bytes[i] != got.string_bytes[i]) {
+ // Print surrounding context.
+ const ctx_start = if (i > 30) i - 30 else 0;
+ std.debug.print(" string_bytes[{d}] first diff (ref=0x{x:0>2} got=0x{x:0>2})\n", .{ i, ref.string_bytes[i], got.string_bytes[i] });
+ std.debug.print(" ref context: \"", .{});
+ for (ctx_start..@min(i + 30, sb_min)) |j| {
+ const ch = ref.string_bytes[j];
+ if (ch >= 0x20 and ch < 0x7f) {
+ std.debug.print("{c}", .{ch});
+ } else {
+ std.debug.print("\\x{x:0>2}", .{ch});
+ }
+ }
+ std.debug.print("\"\n", .{});
+ std.debug.print(" got context: \"", .{});
+ for (ctx_start..@min(i + 30, sb_min)) |j| {
+ const ch = got.string_bytes[j];
+ if (ch >= 0x20 and ch < 0x7f) {
+ std.debug.print("{c}", .{ch});
+ } else {
+ std.debug.print("\\x{x:0>2}", .{ch});
+ }
+ }
+ std.debug.print("\"\n", .{});
return false;
}
}
-
- const ref_sb_len: u32 = @intCast(ref.string_bytes.len);
- if (ref_sb_len != got.string_bytes_len) {
- std.debug.print(" string_bytes_len: ref={d} got={d}\n", .{ ref_sb_len, got.string_bytes_len });
+ if (ref_sb_len2 != got.string_bytes_len) {
+ std.debug.print(" string_bytes_len mismatch: ref={d} got={d} (content matched up to {d})\n", .{ ref_sb_len2, got.string_bytes_len, sb_min });
+ // Print what ref has at the end.
+ if (ref_sb_len2 > got.string_bytes_len) {
+ const extra_start = got.string_bytes_len;
+ std.debug.print(" ref extra at [{d}]: \"", .{extra_start});
+ for (extra_start..@min(extra_start + 60, ref_sb_len2)) |j| {
+ const ch = ref.string_bytes[j];
+ if (ch >= 0x20 and ch < 0x7f) {
+ std.debug.print("{c}", .{ch});
+ } else {
+ std.debug.print("\\x{x:0>2}", .{ch});
+ }
+ }
+ std.debug.print("\"\n", .{});
+ }
return false;
}
- for (0..ref_sb_len) |i| {
- if (ref.string_bytes[i] != got.string_bytes[i]) {
- std.debug.print(" string_bytes[{d}]: ref=0x{x:0>2} got=0x{x:0>2}\n", .{ i, ref.string_bytes[i], got.string_bytes[i] });
- return false;
- }
+
+ const ref_extra_len2: u32 = @intCast(ref.extra.len);
+ if (ref_extra_len2 != got.extra_len) {
+ std.debug.print(" extra_len mismatch: ref={d} got={d}\n", .{ ref_extra_len2, got.extra_len });
+ return false;
}
return true;
@@ -803,7 +852,11 @@ test "astgen: corpus" {
if (true) return error.SkipZigTest;
const gpa = std.testing.allocator;
+ var any_fail = false;
inline for (corpus_files) |entry| {
- try corpusCheck(gpa, entry[0], entry[1]);
+ corpusCheck(gpa, entry[0], entry[1]) catch {
+ any_fail = true;
+ };
}
+ if (any_fail) return error.ZirMismatch;
}