astgen: port Phases 4-5 (control flow, expressions, scope chain)

Port scope chain infrastructure, function parameters, local var_decl,
control flow (if/for/while/switch/orelse/catch/defer), labeled blocks,
break/continue, comparison/boolean/unary operators, array access,
field access rvalue, rvalue type coercion optimization, and many
builtins from upstream AstGen.zig. test_all.zig corpus passes;
4 remaining corpus files still have mismatches (WIP).

Also fix cppcheck/lint issues: safe realloc pattern, null checks,
const correctness, enable inline suppressions, comment out test
debug output for clean `zig build`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 11:40:51 +00:00
parent d6d60fbebf
commit 0295bb4651
4 changed files with 5695 additions and 318 deletions

5897
astgen.c

File diff suppressed because it is too large Load Diff

View File

@@ -608,32 +608,69 @@ fn expectEqualData(
/// Unlike expectEqualZir, does not print diagnostics or return errors.
fn zirMatches(gpa: Allocator, ref: Zir, got: c.Zir) bool {
const ref_len: u32 = @intCast(ref.instructions.len);
if (ref_len != got.inst_len) return false;
if (ref_len != got.inst_len) {
//std.debug.print(" inst_len: ref={d} got={d}\n", .{ ref_len, got.inst_len });
}
const ref_tags = ref.instructions.items(.tag);
const ref_datas = ref.instructions.items(.data);
for (0..ref_len) |i| {
const min_len = @min(ref_len, got.inst_len);
var first_tag_mismatch: ?u32 = null;
for (0..min_len) |i| {
const ref_tag: u8 = @intFromEnum(ref_tags[i]);
const got_tag: u8 = @intCast(got.inst_tags[i]);
if (ref_tag != got_tag) return false;
if (!dataMatches(ref_tags[i], ref_datas[i], got.inst_datas[i])) return false;
if (ref_tag != got_tag) {
first_tag_mismatch = @intCast(i);
break;
}
}
if (first_tag_mismatch) |_| {
//const start = if (ftm > 5) ftm - 5 else 0;
//const end = @min(ftm + 10, 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]);
// const got_tag: u8 = @intCast(got.inst_tags[i]);
// const marker: u8 = if (i == ftm) '>' else ' ';
// 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;
}
}
if (ref_len != got.inst_len) return false;
const ref_extra_len: u32 = @intCast(ref.extra.len);
if (ref_extra_len != got.extra_len) return false;
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;
}
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]) return false;
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] });
return false;
}
}
const ref_sb_len: u32 = @intCast(ref.string_bytes.len);
if (ref_sb_len != got.string_bytes_len) return false;
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 });
return false;
}
for (0..ref_sb_len) |i| {
if (ref.string_bytes[i] != got.string_bytes[i]) return false;
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;
}
}
return true;
@@ -728,7 +765,7 @@ const corpus_files = .{
};
/// Returns .pass or .skip for a single corpus entry.
fn corpusCheck(gpa: Allocator, _: []const u8, source: [:0]const u8) enum { pass, skip } {
fn corpusCheck(gpa: Allocator, name: []const u8, source: [:0]const u8) enum { pass, skip } {
var tree = Ast.parse(gpa, source, .zig) catch return .skip;
defer tree.deinit(gpa);
@@ -740,13 +777,18 @@ fn corpusCheck(gpa: Allocator, _: []const u8, source: [:0]const u8) enum { pass,
var c_zir = c.astGen(&c_ast);
defer c.zirDeinit(&c_zir);
if (c_zir.has_compile_errors) return .skip;
if (c_zir.has_compile_errors) {
//std.debug.print(" -> has_compile_errors\n", .{});
return .skip;
}
if (zirMatches(gpa, ref_zir, c_zir)) {
return .pass;
} else {
//std.debug.print(" -> zir mismatch\n", .{});
return .skip;
}
_ = name;
}
test "astgen: corpus" {

View File

@@ -100,6 +100,7 @@ pub fn build(b: *std.Build) !void {
"--error-exitcode=1",
"--check-level=exhaustive",
"--enable=all",
"--inline-suppr",
"--suppress=missingIncludeSystem",
"--suppress=checkersReport",
"--suppress=unusedFunction", // TODO remove after plumbing is done

53
zir.h
View File

@@ -438,13 +438,64 @@ typedef union {
#define ZIR_REF_NONE UINT32_MAX
#define ZIR_MAIN_STRUCT_INST 0
// Selected Zir.Inst.Ref enum values (matching Zig enum order).
// Zir.Inst.Ref enum values (matching Zig enum order in Zir.zig).
// Types (0-103).
#define ZIR_REF_U1_TYPE 2
#define ZIR_REF_U8_TYPE 3
#define ZIR_REF_I8_TYPE 4
#define ZIR_REF_U16_TYPE 5
#define ZIR_REF_I16_TYPE 6
#define ZIR_REF_U29_TYPE 7
#define ZIR_REF_U32_TYPE 8
#define ZIR_REF_I32_TYPE 9
#define ZIR_REF_U64_TYPE 10
#define ZIR_REF_I64_TYPE 11
#define ZIR_REF_U128_TYPE 13
#define ZIR_REF_I128_TYPE 14
#define ZIR_REF_USIZE_TYPE 16
#define ZIR_REF_ISIZE_TYPE 17
#define ZIR_REF_C_CHAR_TYPE 18
#define ZIR_REF_C_SHORT_TYPE 19
#define ZIR_REF_C_USHORT_TYPE 20
#define ZIR_REF_C_INT_TYPE 21
#define ZIR_REF_C_UINT_TYPE 22
#define ZIR_REF_C_LONG_TYPE 23
#define ZIR_REF_C_ULONG_TYPE 24
#define ZIR_REF_C_LONGLONG_TYPE 25
#define ZIR_REF_C_ULONGLONG_TYPE 26
#define ZIR_REF_C_LONGDOUBLE_TYPE 27
#define ZIR_REF_F16_TYPE 28
#define ZIR_REF_F32_TYPE 29
#define ZIR_REF_F64_TYPE 30
#define ZIR_REF_F80_TYPE 31
#define ZIR_REF_F128_TYPE 32
#define ZIR_REF_ANYOPAQUE_TYPE 33
#define ZIR_REF_BOOL_TYPE 34
#define ZIR_REF_VOID_TYPE 35
#define ZIR_REF_TYPE_TYPE 36
#define ZIR_REF_ANYERROR_TYPE 37
#define ZIR_REF_COMPTIME_INT_TYPE 38
#define ZIR_REF_COMPTIME_FLOAT_TYPE 39
#define ZIR_REF_NORETURN_TYPE 40
#define ZIR_REF_ANYFRAME_TYPE 41
#define ZIR_REF_NULL_TYPE 42
#define ZIR_REF_UNDEFINED_TYPE 43
#define ZIR_REF_ENUM_LITERAL_TYPE 44
#define ZIR_REF_PTR_USIZE_TYPE 45
#define ZIR_REF_PTR_CONST_COMPTIME_INT_TYPE 46
#define ZIR_REF_MANYPTR_U8_TYPE 47
#define ZIR_REF_MANYPTR_CONST_U8_TYPE 48
#define ZIR_REF_MANYPTR_CONST_U8_SENTINEL_0_TYPE 49
#define ZIR_REF_SLICE_CONST_U8_TYPE 50
#define ZIR_REF_SLICE_CONST_U8_SENTINEL_0_TYPE 51
#define ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE 100
#define ZIR_REF_GENERIC_POISON_TYPE 102
#define ZIR_REF_EMPTY_TUPLE_TYPE 103
// Values (104-123).
#define ZIR_REF_UNDEF 104
#define ZIR_REF_UNDEF_BOOL 105
#define ZIR_REF_UNDEF_USIZE 106
#define ZIR_REF_UNDEF_U1 107
#define ZIR_REF_ZERO 108
#define ZIR_REF_ZERO_USIZE 109
#define ZIR_REF_ZERO_U1 110