1690 lines
64 KiB
Zig
1690 lines
64 KiB
Zig
const std = @import("std");
|
|
const Zir = std.zig.Zir;
|
|
const AstGen = std.zig.AstGen;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const parser_test = @import("parser_test.zig");
|
|
const c = parser_test.c;
|
|
|
|
fn refZir(gpa: Allocator, source: [:0]const u8) !Zir {
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var tree = try parser_test.zigAst(gpa, c_ast);
|
|
defer tree.deinit(gpa);
|
|
return try AstGen.generate(gpa, tree);
|
|
}
|
|
|
|
test "astgen dump: simple cases" {
|
|
const gpa = std.testing.allocator;
|
|
|
|
const cases = .{
|
|
.{ "empty", "" },
|
|
.{ "comptime {}", "comptime {}" },
|
|
.{ "const x = 0;", "const x = 0;" },
|
|
.{ "const x = 1;", "const x = 1;" },
|
|
.{ "const x = 0; const y = 0;", "const x = 0; const y = 0;" },
|
|
.{ "test \"t\" {}", "test \"t\" {}" },
|
|
.{ "const std = @import(\"std\");", "const std = @import(\"std\");" },
|
|
.{ "test_all.zig", @embedFile("test_all.zig") },
|
|
};
|
|
|
|
inline for (cases) |case| {
|
|
// std.debug.print("--- {s} ---\n", .{case[0]});
|
|
const source: [:0]const u8 = case[1];
|
|
var zir = try refZir(gpa, source);
|
|
zir.deinit(gpa);
|
|
}
|
|
}
|
|
|
|
/// Build a mask of extra[] indices that should be skipped or specially
|
|
/// handled during comparison. This includes hash data (src_hash or
|
|
/// fields_hash) that are zero-filled in the C output but contain real
|
|
/// Blake3 hashes in the Zig reference, and also undefined padding bits.
|
|
/// Returns a u32 mask array: 0 = skip entirely, 0xFFFFFFFF = compare all bits,
|
|
/// other values = bitmask for partial comparison.
|
|
fn buildHashSkipMask(gpa: Allocator, ref: Zir) ![]u32 {
|
|
const ref_extra_len: u32 = @intCast(ref.extra.len);
|
|
const mask = try gpa.alloc(u32, ref_extra_len);
|
|
@memset(mask, 0xFFFFFFFF);
|
|
|
|
const ref_len: u32 = @intCast(ref.instructions.len);
|
|
const ref_tags = ref.instructions.items(.tag);
|
|
const ref_datas = ref.instructions.items(.data);
|
|
for (0..ref_len) |i| {
|
|
switch (ref_tags[i]) {
|
|
.extended => {
|
|
const ext = ref_datas[i].extended;
|
|
if (ext.opcode == .struct_decl or ext.opcode == .enum_decl or
|
|
ext.opcode == .union_decl or ext.opcode == .opaque_decl)
|
|
{
|
|
// StructDecl/EnumDecl/UnionDecl/OpaqueDecl starts with fields_hash[4].
|
|
const pi = ext.operand;
|
|
for (0..4) |j| mask[pi + j] = 0;
|
|
}
|
|
},
|
|
.declaration => {
|
|
// Declaration starts with src_hash[4].
|
|
const pi = ref_datas[i].declaration.payload_index;
|
|
for (0..4) |j| mask[pi + j] = 0;
|
|
},
|
|
.func, .func_inferred => {
|
|
// Func payload: ret_ty(1) + param_block(1) + body_len(1)
|
|
// + trailing ret_ty + body + SrcLocs(3) + proto_hash(4).
|
|
const pi = ref_datas[i].pl_node.payload_index;
|
|
const ret_ty_raw: u32 = ref.extra[pi];
|
|
const ret_body_len: u32 = ret_ty_raw & 0x7FFFFFFF;
|
|
const body_len: u32 = ref.extra[pi + 2];
|
|
// ret_ty trailing: if body_len > 1, it's a body; if == 1, it's a ref; if 0, void.
|
|
const ret_trailing: u32 = if (ret_body_len > 1) ret_body_len else if (ret_body_len == 1) 1 else 0;
|
|
// proto_hash is at: pi + 3 + ret_trailing + body_len + 3
|
|
if (body_len > 0) {
|
|
const hash_start = pi + 3 + ret_trailing + body_len + 3;
|
|
for (0..4) |j| {
|
|
if (hash_start + j < ref_extra_len)
|
|
mask[hash_start + j] = 0;
|
|
}
|
|
}
|
|
},
|
|
.func_fancy => {
|
|
// FuncFancy: param_block(1) + body_len(1) + bits(1)
|
|
// + trailing cc + ret_ty + noalias + body + SrcLocs(3) + proto_hash(4).
|
|
const pi = ref_datas[i].pl_node.payload_index;
|
|
const body_len: u32 = ref.extra[pi + 1];
|
|
const bits: u32 = ref.extra[pi + 2];
|
|
// FuncFancy.Bits has _:u23 = undefined padding in bits 9..31.
|
|
// Only compare the lower 9 defined bits.
|
|
mask[pi + 2] = 0x1FF;
|
|
var ei: u32 = pi + 3;
|
|
const has_cc_ref: bool = (bits & (1 << 3)) != 0;
|
|
const has_cc_body: bool = (bits & (1 << 4)) != 0;
|
|
const has_ret_ty_ref: bool = (bits & (1 << 5)) != 0;
|
|
const has_ret_ty_body: bool = (bits & (1 << 6)) != 0;
|
|
const has_any_noalias: bool = (bits & (1 << 7)) != 0;
|
|
if (has_cc_body) {
|
|
const cc_body_len = ref.extra[ei];
|
|
ei += 1 + cc_body_len;
|
|
} else if (has_cc_ref) {
|
|
ei += 1;
|
|
}
|
|
if (has_ret_ty_body) {
|
|
const ret_body_len = ref.extra[ei];
|
|
ei += 1 + ret_body_len;
|
|
} else if (has_ret_ty_ref) {
|
|
ei += 1;
|
|
}
|
|
if (has_any_noalias) ei += 1;
|
|
// body + SrcLocs(3) + proto_hash(4)
|
|
if (body_len > 0) {
|
|
const hash_start = ei + body_len + 3;
|
|
for (0..4) |j| {
|
|
if (hash_start + j < ref_extra_len)
|
|
mask[hash_start + j] = 0;
|
|
}
|
|
}
|
|
},
|
|
.float128 => {
|
|
// Float128 payload: 4 u32 pieces encoding the binary128 value.
|
|
// The C implementation parses floats with strtold (80-bit extended
|
|
// precision on x86-64), then converts to binary128 layout. The bottom
|
|
// 49 of 112 fraction bits are zero-padded. Skip all 4 pieces.
|
|
const pi = ref_datas[i].pl_node.payload_index;
|
|
for (0..4) |j| {
|
|
if (pi + j < ref_extra_len)
|
|
mask[pi + j] = 0;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
test "astgen: empty source" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: comptime {}" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "comptime {}";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: const x = 0;" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const x = 0;";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: const x = 1;" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const x = 1;";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: const x = 0; const y = 0;" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const x = 0; const y = 0;";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: field_access" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const std = @import(\"std\");\nconst mem = std.mem;";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: addr array init" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const x = &[_][]const u8{\"a\",\"b\"};";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: test empty body" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "test \"t\" {}";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: test_all.zig" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = @embedFile("test_all.zig");
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: array init with sentinel" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const std = @import("std");
|
|
\\test "t" {
|
|
\\ try std.testing.expect(@TypeOf([_]u8{}) == [0]u8);
|
|
\\ try std.testing.expect(@TypeOf([_:0]u8{}) == [0:0]u8);
|
|
\\}
|
|
;
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: type.zig up to Opaque" {
|
|
const gpa = std.testing.allocator;
|
|
const source = @embedFile("../test/behavior/type.zig");
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: labeled block in comptime" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\test "t" {
|
|
\\ const x = comptime blk: {
|
|
\\ break :blk @as(u32, 42);
|
|
\\ };
|
|
\\ _ = x;
|
|
\\}
|
|
;
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: @TypeOf multi-arg" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\test "typeof_peer" {
|
|
\\ var x: u8 = 0;
|
|
\\ var y: u16 = 0;
|
|
\\ _ = .{ &x, &y };
|
|
\\ _ = @TypeOf(x, y);
|
|
\\}
|
|
;
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: @import" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const std = @import(\"std\");";
|
|
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
pub fn expectEqualZir(gpa: Allocator, ref: Zir, got: c.Zir) !void {
|
|
const ref_len: u32 = @intCast(ref.instructions.len);
|
|
const ref_tags = ref.instructions.items(.tag);
|
|
const ref_datas = ref.instructions.items(.data);
|
|
|
|
// 1. Compare lengths.
|
|
if (ref_len != got.inst_len) {
|
|
std.debug.print("inst_len mismatch: ref={d} got={d}\n", .{ ref_len, got.inst_len });
|
|
// Print all ref declaration positions.
|
|
{
|
|
var prev_ref: usize = 0;
|
|
var prev_got: usize = 0;
|
|
var ri: usize = 0;
|
|
var gi: usize = 0;
|
|
while (ri < ref_len and gi < got.inst_len) {
|
|
// Find next decl in ref
|
|
while (ri < ref_len and ref_tags[ri] != .declaration) ri += 1;
|
|
// Find next decl in got
|
|
while (gi < got.inst_len and got.inst_tags[gi] != 44) gi += 1;
|
|
if (ri < ref_len and gi < got.inst_len) {
|
|
const ref_span = ri - prev_ref;
|
|
const got_span = gi - prev_got;
|
|
{
|
|
const diff: i32 = @as(i32, @intCast(got_span)) - @as(i32, @intCast(ref_span));
|
|
if (diff != 0) {
|
|
std.debug.print(" DIFF: decl ref[{d}] got[{d}] src_node={d}: ref_span={d} got_span={d} (diff={d})\n", .{
|
|
ri, gi, ref_datas[ri].declaration.src_node,
|
|
ref_span, got_span, diff,
|
|
});
|
|
}
|
|
}
|
|
prev_ref = ri;
|
|
prev_got = gi;
|
|
ri += 1;
|
|
gi += 1;
|
|
}
|
|
}
|
|
}
|
|
var ref_counts: [265]u32 = .{0} ** 265;
|
|
var got_counts: [265]u32 = .{0} ** 265;
|
|
for (0..ref_len) |i| ref_counts[@intFromEnum(ref_tags[i])] += 1;
|
|
for (0..got.inst_len) |i| got_counts[got.inst_tags[i]] += 1;
|
|
for (0..265) |t| {
|
|
if (ref_counts[t] != got_counts[t])
|
|
std.debug.print("tag {d}: ref={d} got={d} (diff={d})\n", .{
|
|
t, ref_counts[t], got_counts[t],
|
|
@as(i32, @intCast(got_counts[t])) - @as(i32, @intCast(ref_counts[t])),
|
|
});
|
|
}
|
|
// Find first tag divergence.
|
|
const min_len = @min(ref_len, got.inst_len);
|
|
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) {
|
|
std.debug.print("first divergence at [{d}]: ref_tag={d} got_tag={d}\n", .{ i, ref_tag, got_tag });
|
|
// Print window of tags around divergence for debugging.
|
|
const window_start = if (i >= 20) i - 20 else 0;
|
|
const window_end = @min(i + 20, min_len);
|
|
std.debug.print(" ref tags [{d}..{d}]: ", .{ window_start, window_end });
|
|
for (window_start..window_end) |wi| {
|
|
std.debug.print("{d} ", .{@intFromEnum(ref_tags[wi])});
|
|
}
|
|
std.debug.print("\n got tags [{d}..{d}]: ", .{ window_start, window_end });
|
|
for (window_start..window_end) |wi| {
|
|
if (wi < got.inst_len) {
|
|
std.debug.print("{d} ", .{got.inst_tags[wi]});
|
|
}
|
|
}
|
|
std.debug.print("\n", .{});
|
|
// Data printing skipped to avoid tagged union safety panics.
|
|
// Scan for nearest declaration.
|
|
var j: usize = i;
|
|
while (j > 0) {
|
|
j -= 1;
|
|
if (ref_tags[j] == .declaration) {
|
|
std.debug.print(" nearest decl at [{d}]: src_node={d}\n", .{
|
|
j, ref_datas[j].declaration.src_node,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return error.TestExpectedEqual;
|
|
}
|
|
|
|
// 2. Compare instruction tags.
|
|
for (0..ref_len) |i| {
|
|
const ref_tag: u8 = @intFromEnum(ref_tags[i]);
|
|
const got_tag: u8 = @intCast(got.inst_tags[i]);
|
|
if (ref_tag != got_tag) {
|
|
std.debug.print(
|
|
"inst_tags[{d}] mismatch: ref={d} got={d}\n",
|
|
.{ i, ref_tag, got_tag },
|
|
);
|
|
// Print surrounding tag window.
|
|
const window_start = if (i >= 20) i - 20 else 0;
|
|
const window_end = @min(i + 20, ref_len);
|
|
std.debug.print(" ref tags [{d}..{d}]: ", .{ window_start, window_end });
|
|
for (window_start..window_end) |wi| {
|
|
std.debug.print("{d} ", .{@intFromEnum(ref_tags[wi])});
|
|
}
|
|
std.debug.print("\n got tags [{d}..{d}]: ", .{ window_start, window_end });
|
|
for (window_start..window_end) |wi| {
|
|
if (wi < got.inst_len) {
|
|
std.debug.print("{d} ", .{got.inst_tags[wi]});
|
|
}
|
|
}
|
|
std.debug.print("\n", .{});
|
|
// Find nearest decl.
|
|
var j: usize = i;
|
|
while (j > 0) {
|
|
j -= 1;
|
|
if (ref_tags[j] == .declaration) {
|
|
std.debug.print(" nearest decl at [{d}]: src_node={d}\n", .{
|
|
j, ref_datas[j].declaration.src_node,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
return error.TestExpectedEqual;
|
|
}
|
|
}
|
|
|
|
// 2.5. If extra_len differs and is small, dump both extra arrays before step 3.
|
|
{
|
|
const ref_extra_len_dump: u32 = @intCast(ref.extra.len);
|
|
if (ref_extra_len_dump != got.extra_len and ref_extra_len_dump < 200) {
|
|
std.debug.print("EXTRA DUMP (ref_len={d}, got_len={d}):\n", .{ ref_extra_len_dump, got.extra_len });
|
|
const max_el = @max(ref_extra_len_dump, got.extra_len);
|
|
for (0..max_el) |ei| {
|
|
const rv: i64 = if (ei < ref_extra_len_dump) @intCast(ref.extra[ei]) else -1;
|
|
const gv: i64 = if (ei < got.extra_len) @intCast(got.extra[ei]) else -1;
|
|
const marker: u8 = if (rv != gv) '!' else ' ';
|
|
std.debug.print(" extra[{d:>3}]: ref={d:>10} got={d:>10} {c}\n", .{ ei, rv, gv, marker });
|
|
}
|
|
std.debug.print("TAGS (ref_inst_count={d}, got_inst_count={d}):\n", .{ ref_len, got.inst_len });
|
|
for (0..ref_len) |ti| {
|
|
const rt: u8 = @intFromEnum(ref_tags[ti]);
|
|
const gt: u8 = @intCast(got.inst_tags[ti]);
|
|
const tmarker: u8 = if (rt != gt) '!' else ' ';
|
|
std.debug.print(" inst[{d:>3}]: ref_tag={d:>3} got_tag={d:>3} {c}", .{ ti, rt, gt, tmarker });
|
|
if (ref_tags[ti] == .declaration) {
|
|
std.debug.print(" decl: ref_pi={d} got_pi={d}", .{
|
|
ref_datas[ti].declaration.payload_index,
|
|
got.inst_datas[ti].declaration.payload_index,
|
|
});
|
|
}
|
|
if (ref_tags[ti] == .extended) {
|
|
const ref_ext = ref_datas[ti].extended;
|
|
std.debug.print(" ext: ref_op={d} got_op={d} ref_small=0x{x} got_small=0x{x}", .{
|
|
ref_ext.operand,
|
|
got.inst_datas[ti].extended.operand,
|
|
ref_ext.small,
|
|
got.inst_datas[ti].extended.small,
|
|
});
|
|
}
|
|
std.debug.print("\n", .{});
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Compare instruction data field-by-field.
|
|
for (0..ref_len) |i| {
|
|
expectEqualData(i, ref_tags[i], ref_datas[i], got.inst_datas[i]) catch {
|
|
// Print extra data lengths for debugging.
|
|
const ref_extra_len2: u32 = @intCast(ref.extra.len);
|
|
std.debug.print(" extra_len: ref={d} got={d} (diff={d})\n", .{
|
|
ref_extra_len2,
|
|
got.extra_len,
|
|
@as(i64, got.extra_len) - @as(i64, ref_extra_len2),
|
|
});
|
|
// Find the first instruction where any raw data word differs.
|
|
// Find first extra data divergence by scanning both extra arrays.
|
|
{
|
|
const ref_extra_arr = ref.extra;
|
|
const min_extra = @min(ref_extra_arr.len, got.extra_len);
|
|
for (0..min_extra) |eidx| {
|
|
if (ref_extra_arr[eidx] != got.extra[eidx]) {
|
|
std.debug.print(" first extra divergence at extra[{d}]: ref=0x{x:0>8} got=0x{x:0>8}\n", .{
|
|
eidx, ref_extra_arr[eidx], got.extra[eidx],
|
|
});
|
|
// Print surrounding context.
|
|
const ctx_s = if (eidx >= 5) eidx - 5 else 0;
|
|
const ctx_e = @min(eidx + 10, min_extra);
|
|
for (ctx_s..ctx_e) |ci| {
|
|
const marker: u8 = if (ref_extra_arr[ci] != got.extra[ci]) '!' else ' ';
|
|
std.debug.print(" extra[{d}]: ref={d} (0x{x:0>8}) got={d} (0x{x:0>8}) {c}\n", .{
|
|
ci, ref_extra_arr[ci], ref_extra_arr[ci], got.extra[ci], got.extra[ci], marker,
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Find the first break instruction where payload_index diverges.
|
|
// Break instructions store payload_index in the second u32.
|
|
var found_first = false;
|
|
for (0..ref_len) |k| {
|
|
if (ref_tags[k] == .break_inline or ref_tags[k] == .@"break") {
|
|
const r_pi = ref_datas[k].@"break".payload_index;
|
|
const g_pi = got.inst_datas[k].break_data.payload_index;
|
|
if (r_pi != g_pi and !found_first) {
|
|
found_first = true;
|
|
std.debug.print(" first break payload divergence at inst[{d}]: ref_pi={d} got_pi={d} (diff={d})\n", .{
|
|
k, r_pi, g_pi,
|
|
@as(i64, g_pi) - @as(i64, r_pi),
|
|
});
|
|
// Find nearest decl
|
|
var dk: usize = k;
|
|
while (dk > 0) {
|
|
dk -= 1;
|
|
if (ref_tags[dk] == .declaration) {
|
|
std.debug.print(" nearest decl at [{d}]: src_node={d}\n", .{
|
|
dk, ref_datas[dk].declaration.src_node,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Also check func instructions
|
|
for (0..ref_len) |k| {
|
|
if (ref_tags[k] == .func or ref_tags[k] == .func_inferred or
|
|
ref_tags[k] == .func_fancy)
|
|
{
|
|
const r_pi = ref_datas[k].pl_node.payload_index;
|
|
const g_pi = got.inst_datas[k].pl_node.payload_index;
|
|
if (r_pi != g_pi) {
|
|
std.debug.print(" func payload divergence at inst[{d}] tag={d}: ref_pi={d} got_pi={d} (diff={d})\n", .{
|
|
k, @intFromEnum(ref_tags[k]), r_pi, g_pi,
|
|
@as(i64, g_pi) - @as(i64, r_pi),
|
|
});
|
|
// Find nearest decl
|
|
var dk: usize = k;
|
|
while (dk > 0) {
|
|
dk -= 1;
|
|
if (ref_tags[dk] == .declaration) {
|
|
std.debug.print(" nearest decl at [{d}]: src_node={d}\n", .{
|
|
dk, ref_datas[dk].declaration.src_node,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Print nearest declaration for context.
|
|
var j: usize = i;
|
|
while (j > 0) {
|
|
j -= 1;
|
|
if (ref_tags[j] == .declaration) {
|
|
std.debug.print(" nearest decl at [{d}]: src_node={d}\n", .{
|
|
j, ref_datas[j].declaration.src_node,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
// Print what tags are at the operand positions if break_inline.
|
|
if (ref_tags[i] == .break_inline) {
|
|
const r_op = @intFromEnum(ref_datas[i].@"break".operand);
|
|
const g_op = got.inst_datas[i].break_data.operand;
|
|
if (r_op >= 124 and r_op - 124 < ref_len) {
|
|
std.debug.print(" ref operand inst[{d}] tag={d}\n", .{
|
|
r_op - 124, @intFromEnum(ref_tags[r_op - 124]),
|
|
});
|
|
}
|
|
if (g_op >= 124 and g_op - 124 < ref_len) {
|
|
std.debug.print(" got operand inst[{d}] tag={d}\n", .{
|
|
g_op - 124, @intFromEnum(ref_tags[g_op - 124]),
|
|
});
|
|
}
|
|
}
|
|
// Also compare string bytes to diagnose string table divergence.
|
|
{
|
|
const ref_sb_len2: u32 = @intCast(ref.string_bytes.len);
|
|
const sb_min = @min(ref_sb_len2, got.string_bytes_len);
|
|
std.debug.print(" string_bytes_len: ref={d} got={d}\n", .{ ref_sb_len2, got.string_bytes_len });
|
|
for (0..sb_min) |si| {
|
|
if (ref.string_bytes[si] != got.string_bytes[si]) {
|
|
// Print context around divergence.
|
|
const ctx_start = if (si >= 20) si - 20 else 0;
|
|
const ctx_end = @min(si + 20, sb_min);
|
|
std.debug.print(" first string_bytes divergence at [{d}]:\n", .{si});
|
|
std.debug.print(" ref: ", .{});
|
|
for (ctx_start..ctx_end) |ci| std.debug.print("{c}", .{if (ref.string_bytes[ci] >= 0x20 and ref.string_bytes[ci] < 0x7f) ref.string_bytes[ci] else '.'});
|
|
std.debug.print("\n got: ", .{});
|
|
for (ctx_start..ctx_end) |ci| {
|
|
if (ci < got.string_bytes_len) {
|
|
const ch = got.string_bytes[ci];
|
|
std.debug.print("{c}", .{if (ch >= 0x20 and ch < 0x7f) ch else '.'});
|
|
}
|
|
}
|
|
std.debug.print("\n", .{});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return error.TestExpectedEqual;
|
|
};
|
|
}
|
|
// 4. Compare string bytes.
|
|
const ref_sb_len: u32 = @intCast(ref.string_bytes.len);
|
|
try std.testing.expectEqual(ref_sb_len, got.string_bytes_len);
|
|
for (0..ref_sb_len) |i| {
|
|
if (ref.string_bytes[i] != got.string_bytes[i]) {
|
|
std.debug.print(
|
|
"string_bytes[{d}] mismatch: ref=0x{x:0>2} got=0x{x:0>2}\n",
|
|
.{ i, ref.string_bytes[i], got.string_bytes[i] },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
}
|
|
|
|
// 5. Compare extra data (skipping hash positions, masking undefined bits).
|
|
const cmp_mask = try buildHashSkipMask(gpa, ref);
|
|
defer gpa.free(cmp_mask);
|
|
const ref_extra_len: u32 = @intCast(ref.extra.len);
|
|
try std.testing.expectEqual(ref_extra_len, got.extra_len);
|
|
for (0..ref_extra_len) |i| {
|
|
if (cmp_mask[i] == 0) continue;
|
|
if ((ref.extra[i] & cmp_mask[i]) != (got.extra[i] & cmp_mask[i])) {
|
|
// Show first 10 extra diffs.
|
|
var count: u32 = 0;
|
|
for (0..ref_extra_len) |j| {
|
|
if (cmp_mask[j] != 0 and (ref.extra[j] & cmp_mask[j]) != (got.extra[j] & cmp_mask[j])) {
|
|
std.debug.print(
|
|
"extra[{d}] mismatch: ref={d} (0x{x}) got={d} (0x{x})\n",
|
|
.{ j, ref.extra[j], ref.extra[j], got.extra[j], got.extra[j] },
|
|
);
|
|
count += 1;
|
|
if (count >= 10) break;
|
|
}
|
|
}
|
|
// Find owning instruction for the first mismatch position.
|
|
{
|
|
var best_inst: ?usize = null;
|
|
var best_pi: u32 = 0;
|
|
for (0..ref_len) |ii| {
|
|
const pi: u32 = switch (ref_tags[ii]) {
|
|
.declaration => ref_datas[ii].declaration.payload_index,
|
|
.func, .func_inferred, .func_fancy => ref_datas[ii].pl_node.payload_index,
|
|
.extended => blk: {
|
|
const ext = ref_datas[ii].extended;
|
|
break :blk ext.operand;
|
|
},
|
|
else => continue,
|
|
};
|
|
if (pi <= i and (best_inst == null or pi > best_pi)) {
|
|
best_inst = ii;
|
|
best_pi = pi;
|
|
}
|
|
}
|
|
if (best_inst) |bi| {
|
|
const ext_info: []const u8 = if (ref_tags[bi] == .extended) blk: {
|
|
break :blk @tagName(ref_datas[bi].extended.opcode);
|
|
} else "";
|
|
std.debug.print(" owning inst[{d}] tag={s} payload_start={d} (offset_in_payload={d}) {s}\n", .{
|
|
bi, @tagName(ref_tags[bi]), best_pi, i - best_pi, ext_info,
|
|
});
|
|
// Find nearest decl
|
|
var dk: usize = bi;
|
|
while (dk > 0) {
|
|
dk -= 1;
|
|
if (ref_tags[dk] == .declaration) {
|
|
std.debug.print(" nearest decl at [{d}]: src_node={d}\n", .{
|
|
dk, ref_datas[dk].declaration.src_node,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Print context around the mismatch position.
|
|
const ctx_start = if (i >= 5) i - 5 else 0;
|
|
const ctx_end = @min(i + 10, ref_extra_len);
|
|
std.debug.print(" ref extra[{d}..{d}]: ", .{ ctx_start, ctx_end });
|
|
for (ctx_start..ctx_end) |ci| std.debug.print("{d} ", .{ref.extra[ci]});
|
|
std.debug.print("\n got extra[{d}..{d}]: ", .{ ctx_start, ctx_end });
|
|
for (ctx_start..ctx_end) |ci| std.debug.print("{d} ", .{got.extra[ci]});
|
|
std.debug.print("\n mask[{d}..{d}]: ", .{ ctx_start, ctx_end });
|
|
for (ctx_start..ctx_end) |ci| std.debug.print("{d} ", .{cmp_mask[ci]});
|
|
std.debug.print("\n", .{});
|
|
// Dump ALL extra data for small tests.
|
|
if (ref_extra_len < 200) {
|
|
std.debug.print(" ALL ref extra ({d}):\n ", .{ref_extra_len});
|
|
for (0..ref_extra_len) |ei| {
|
|
std.debug.print("{d} ", .{ref.extra[ei]});
|
|
if ((ei + 1) % 20 == 0) std.debug.print("\n ", .{});
|
|
}
|
|
std.debug.print("\n ALL got extra ({d}):\n ", .{got.extra_len});
|
|
for (0..got.extra_len) |ei| {
|
|
std.debug.print("{d} ", .{got.extra[ei]});
|
|
if ((ei + 1) % 20 == 0) std.debug.print("\n ", .{});
|
|
}
|
|
std.debug.print("\n ALL mask:\n ", .{});
|
|
for (0..ref_extra_len) |ei| {
|
|
if (cmp_mask[ei] == 0) std.debug.print("S ", .{}) else std.debug.print(". ", .{});
|
|
if ((ei + 1) % 20 == 0) std.debug.print("\n ", .{});
|
|
}
|
|
std.debug.print("\n ALL tags ({d}):\n ", .{ref_len});
|
|
for (0..ref_len) |ti| {
|
|
std.debug.print("{d} ", .{@intFromEnum(ref_tags[ti])});
|
|
}
|
|
std.debug.print("\n", .{});
|
|
}
|
|
}
|
|
return error.TestExpectedEqual;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Compare a single instruction's data, dispatching by tag.
|
|
/// Zig's Data union has no guaranteed in-memory layout, so we
|
|
/// compare each variant's fields individually.
|
|
fn expectEqualData(
|
|
idx: usize,
|
|
tag: Zir.Inst.Tag,
|
|
ref: Zir.Inst.Data,
|
|
got: c.ZirInstData,
|
|
) !void {
|
|
switch (tag) {
|
|
.extended => {
|
|
const r = ref.extended;
|
|
const g = got.extended;
|
|
// Some extended opcodes have undefined/unused small+operand.
|
|
const skip_data = switch (r.opcode) {
|
|
.dbg_empty_stmt, .astgen_error => true,
|
|
else => false,
|
|
};
|
|
const skip_small = switch (r.opcode) {
|
|
// Container decl Small packed structs have undefined padding bits.
|
|
.struct_decl,
|
|
.enum_decl,
|
|
.union_decl,
|
|
.opaque_decl,
|
|
// addNodeExtended sets small = undefined (AstGen.zig:12775).
|
|
.this,
|
|
.ret_addr,
|
|
.error_return_trace,
|
|
.frame,
|
|
.frame_address,
|
|
.breakpoint,
|
|
.disable_instrumentation,
|
|
.disable_intrinsics,
|
|
.in_comptime,
|
|
.c_va_start,
|
|
// addExtendedPayload passes undefined for small (AstGen.zig:12393).
|
|
.builtin_extern,
|
|
.set_float_mode,
|
|
.builtin_src,
|
|
.int_from_error,
|
|
.error_from_int,
|
|
.error_cast,
|
|
.wasm_memory_size,
|
|
.wasm_memory_grow,
|
|
.c_define,
|
|
.c_undef,
|
|
.c_include,
|
|
.select,
|
|
.prefetch,
|
|
.c_va_arg,
|
|
.c_va_copy,
|
|
.c_va_end,
|
|
.work_item_id,
|
|
.work_group_size,
|
|
.work_group_id,
|
|
.add_with_overflow,
|
|
.sub_with_overflow,
|
|
.mul_with_overflow,
|
|
.shl_with_overflow,
|
|
.restore_err_ret_index,
|
|
.branch_hint,
|
|
.compile_log,
|
|
.field_parent_ptr,
|
|
=> true,
|
|
else => false,
|
|
};
|
|
if (@intFromEnum(r.opcode) != g.opcode or
|
|
(!skip_data and !skip_small and r.small != g.small) or
|
|
(!skip_data and r.operand != g.operand))
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (extended) mismatch:\n" ++
|
|
" ref: opcode={d} small=0x{x:0>4} operand={d}\n" ++
|
|
" got: opcode={d} small=0x{x:0>4} operand={d}\n",
|
|
.{
|
|
idx,
|
|
@intFromEnum(r.opcode),
|
|
r.small,
|
|
r.operand,
|
|
g.opcode,
|
|
g.small,
|
|
g.operand,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.declaration => {
|
|
const r = ref.declaration;
|
|
const g = got.declaration;
|
|
if (@intFromEnum(r.src_node) != g.src_node or
|
|
r.payload_index != g.payload_index)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (declaration) mismatch:\n" ++
|
|
" ref: src_node={d} payload_index={d}\n" ++
|
|
" got: src_node={d} payload_index={d}\n",
|
|
.{
|
|
idx,
|
|
@intFromEnum(r.src_node),
|
|
r.payload_index,
|
|
g.src_node,
|
|
g.payload_index,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.break_inline => {
|
|
const r = ref.@"break";
|
|
const g = got.break_data;
|
|
if (@intFromEnum(r.operand) != g.operand or
|
|
r.payload_index != g.payload_index)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (break_inline) mismatch:\n" ++
|
|
" ref: operand={d} payload_index={d}\n" ++
|
|
" got: operand={d} payload_index={d}\n",
|
|
.{
|
|
idx,
|
|
@intFromEnum(r.operand),
|
|
r.payload_index,
|
|
g.operand,
|
|
g.payload_index,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.import => {
|
|
const r = ref.pl_tok;
|
|
const g = got.pl_tok;
|
|
if (@intFromEnum(r.src_tok) != g.src_tok or
|
|
r.payload_index != g.payload_index)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (import) mismatch:\n" ++
|
|
" ref: src_tok={d} payload_index={d}\n" ++
|
|
" got: src_tok={d} payload_index={d}\n",
|
|
.{
|
|
idx,
|
|
@intFromEnum(r.src_tok),
|
|
r.payload_index,
|
|
g.src_tok,
|
|
g.payload_index,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.dbg_stmt => {
|
|
const r = ref.dbg_stmt;
|
|
const g = got.dbg_stmt;
|
|
if (r.line != g.line or r.column != g.column) {
|
|
std.debug.print(
|
|
"inst_datas[{d}] (dbg_stmt) mismatch:\n" ++
|
|
" ref: line={d} column={d}\n" ++
|
|
" got: line={d} column={d}\n",
|
|
.{ idx, r.line, r.column, g.line, g.column },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.ensure_result_non_error,
|
|
.restore_err_ret_index_unconditional,
|
|
.validate_struct_init_ty,
|
|
.validate_struct_init_result_ty,
|
|
.struct_init_empty_result,
|
|
.struct_init_empty,
|
|
.struct_init_empty_ref_result,
|
|
=> {
|
|
const r = ref.un_node;
|
|
const g = got.un_node;
|
|
if (@intFromEnum(r.src_node) != g.src_node or
|
|
@intFromEnum(r.operand) != g.operand)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] ({s}) mismatch:\n" ++
|
|
" ref: src_node={d} operand={d}\n" ++
|
|
" got: src_node={d} operand={d}\n",
|
|
.{
|
|
idx,
|
|
@tagName(tag),
|
|
@intFromEnum(r.src_node),
|
|
@intFromEnum(r.operand),
|
|
g.src_node,
|
|
g.operand,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.ret_implicit => {
|
|
const r = ref.un_tok;
|
|
const g = got.un_tok;
|
|
if (@intFromEnum(r.src_tok) != g.src_tok or
|
|
@intFromEnum(r.operand) != g.operand)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (ret_implicit) mismatch:\n" ++
|
|
" ref: src_tok={d} operand={d}\n" ++
|
|
" got: src_tok={d} operand={d}\n",
|
|
.{
|
|
idx,
|
|
@intFromEnum(r.src_tok),
|
|
@intFromEnum(r.operand),
|
|
g.src_tok,
|
|
g.operand,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.func,
|
|
.func_inferred,
|
|
.func_fancy,
|
|
.array_type,
|
|
.array_type_sentinel,
|
|
.array_cat,
|
|
.array_init,
|
|
.array_init_ref,
|
|
.error_set_decl,
|
|
.struct_init_field_type,
|
|
.struct_init,
|
|
.struct_init_ref,
|
|
.validate_array_init_ref_ty,
|
|
.validate_array_init_ty,
|
|
=> {
|
|
const r = ref.pl_node;
|
|
const g = got.pl_node;
|
|
if (@intFromEnum(r.src_node) != g.src_node or
|
|
r.payload_index != g.payload_index)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] ({s}) mismatch:\n" ++
|
|
" ref: src_node={d} payload_index={d}\n" ++
|
|
" got: src_node={d} payload_index={d}\n",
|
|
.{
|
|
idx,
|
|
@tagName(tag),
|
|
@intFromEnum(r.src_node),
|
|
r.payload_index,
|
|
g.src_node,
|
|
g.payload_index,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.decl_val, .decl_ref => {
|
|
const r = ref.str_tok;
|
|
const g = got.str_tok;
|
|
if (@intFromEnum(r.start) != g.start or @intFromEnum(r.src_tok) != g.src_tok) {
|
|
std.debug.print(
|
|
"inst_datas[{d}] ({s}) mismatch:\n" ++
|
|
" ref: start={d} src_tok={d}\n" ++
|
|
" got: start={d} src_tok={d}\n",
|
|
.{
|
|
idx,
|
|
@tagName(tag),
|
|
@intFromEnum(r.start),
|
|
@intFromEnum(r.src_tok),
|
|
g.start,
|
|
g.src_tok,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.field_val, .field_ptr, .field_val_named, .field_ptr_named => {
|
|
const r = ref.pl_node;
|
|
const g = got.pl_node;
|
|
if (@intFromEnum(r.src_node) != g.src_node or
|
|
r.payload_index != g.payload_index)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] ({s}) mismatch:\n" ++
|
|
" ref: src_node={d} payload_index={d}\n" ++
|
|
" got: src_node={d} payload_index={d}\n",
|
|
.{
|
|
idx,
|
|
@tagName(tag),
|
|
@intFromEnum(r.src_node),
|
|
r.payload_index,
|
|
g.src_node,
|
|
g.payload_index,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.int => {
|
|
if (ref.int != got.int_val) {
|
|
std.debug.print(
|
|
"inst_datas[{d}] (int) mismatch: ref={d} got={d}\n",
|
|
.{ idx, ref.int, got.int_val },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.ptr_type => {
|
|
// Compare ptr_type data: flags, size, payload_index.
|
|
if (@as(u8, @bitCast(ref.ptr_type.flags)) != got.ptr_type.flags or
|
|
@intFromEnum(ref.ptr_type.size) != got.ptr_type.size or
|
|
ref.ptr_type.payload_index != got.ptr_type.payload_index)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (ptr_type) mismatch:\n" ++
|
|
" ref: flags=0x{x} size={d} pi={d}\n" ++
|
|
" got: flags=0x{x} size={d} pi={d}\n",
|
|
.{
|
|
idx,
|
|
@as(u8, @bitCast(ref.ptr_type.flags)),
|
|
@intFromEnum(ref.ptr_type.size),
|
|
ref.ptr_type.payload_index,
|
|
got.ptr_type.flags,
|
|
got.ptr_type.size,
|
|
got.ptr_type.payload_index,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.int_type => {
|
|
const r = ref.int_type;
|
|
const g = got.int_type;
|
|
if (@intFromEnum(r.src_node) != g.src_node or
|
|
@intFromEnum(r.signedness) != g.signedness or
|
|
r.bit_count != g.bit_count)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (int_type) mismatch\n",
|
|
.{idx},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
.str => {
|
|
const r = ref.str;
|
|
const g = got.str;
|
|
if (@intFromEnum(r.start) != g.start or r.len != g.len) {
|
|
std.debug.print(
|
|
"inst_datas[{d}] (str) mismatch:\n" ++
|
|
" ref: start={d} len={d}\n" ++
|
|
" got: start={d} len={d}\n",
|
|
.{ idx, @intFromEnum(r.start), r.len, g.start, g.len },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
else => {
|
|
// Generic raw comparison: treat data as two u32 words.
|
|
// Tags using .node data format have undefined second word.
|
|
const ref_raw = @as([*]const u32, @ptrCast(&ref));
|
|
const got_raw = @as([*]const u32, @ptrCast(&got));
|
|
// Tags where only the first u32 word is meaningful
|
|
// (second word is padding/undefined).
|
|
const first_word_only = switch (tag) {
|
|
// .node data format (single i32):
|
|
.repeat,
|
|
.repeat_inline,
|
|
.ret_ptr,
|
|
.ret_type,
|
|
.trap,
|
|
.alloc_inferred,
|
|
.alloc_inferred_mut,
|
|
.alloc_inferred_comptime,
|
|
.alloc_inferred_comptime_mut,
|
|
// .@"unreachable" data format (src_node + padding):
|
|
.@"unreachable",
|
|
// .save_err_ret_index data format (operand only):
|
|
.save_err_ret_index,
|
|
// .float data format (f32 = 4 bytes, second word is padding):
|
|
.float,
|
|
// .elem_val_imm data format (u32 + u8, 3 bytes padding):
|
|
.elem_val_imm,
|
|
=> true,
|
|
else => false,
|
|
};
|
|
const w1_match = ref_raw[0] == got_raw[0];
|
|
const w2_match = first_word_only or ref_raw[1] == got_raw[1];
|
|
if (!w1_match or !w2_match) {
|
|
std.debug.print(
|
|
"inst_datas[{d}] ({s}) raw mismatch:\n" ++
|
|
" ref: 0x{x:0>8} 0x{x:0>8}\n" ++
|
|
" got: 0x{x:0>8} 0x{x:0>8}\n",
|
|
.{
|
|
idx,
|
|
@tagName(tag),
|
|
ref_raw[0],
|
|
ref_raw[1],
|
|
got_raw[0],
|
|
got_raw[1],
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
const corpus_files = .{
|
|
"astgen_test.zig",
|
|
"../build.zig",
|
|
"parser_test.zig",
|
|
"test_all.zig",
|
|
"tokenizer_test.zig",
|
|
|
|
"../test/behavior/optional.zig",
|
|
"../test/behavior/call.zig",
|
|
"../test/behavior/pointers.zig",
|
|
"../test/behavior/type.zig",
|
|
"../test/behavior/enum.zig",
|
|
"../test/behavior/switch_on_captured_error.zig",
|
|
"../test/behavior/error.zig",
|
|
"../test/behavior/switch.zig",
|
|
"../test/behavior/array.zig",
|
|
"../test/behavior/slice.zig",
|
|
"../test/behavior/basic.zig",
|
|
"../test/behavior/packed-struct.zig",
|
|
"../test/behavior/eval.zig",
|
|
"../test/behavior/field_parent_ptr.zig",
|
|
"../test/behavior/struct.zig",
|
|
"../test/behavior/floatop.zig",
|
|
"../test/behavior/union.zig",
|
|
"../test/behavior/math.zig",
|
|
"../test/behavior/vector.zig",
|
|
"../test/behavior/cast.zig",
|
|
"../test/behavior/abs.zig",
|
|
"../test/behavior/addrspace_and_linksection.zig",
|
|
"../test/behavior/align.zig",
|
|
"../test/behavior/alignof.zig",
|
|
"../test/behavior/asm.zig",
|
|
"../test/behavior/atomics.zig",
|
|
"../test/behavior/bitcast.zig",
|
|
"../test/behavior/bitreverse.zig",
|
|
"../test/behavior/bit_shifting.zig",
|
|
"../test/behavior/bool.zig",
|
|
"../test/behavior/builtin_functions_returning_void_or_noreturn.zig",
|
|
"../test/behavior/byteswap.zig",
|
|
"../test/behavior/byval_arg_var.zig",
|
|
"../test/behavior/cast_int.zig",
|
|
"../test/behavior/comptime_memory.zig",
|
|
"../test/behavior/const_slice_child.zig",
|
|
"../test/behavior/decl_literals.zig",
|
|
"../test/behavior/decltest.zig",
|
|
"../test/behavior/defer.zig",
|
|
"../test/behavior/destructure.zig",
|
|
"../test/behavior/duplicated_test_names.zig",
|
|
"../test/behavior/empty_union.zig",
|
|
"../test/behavior/export_builtin.zig",
|
|
"../test/behavior/export_c_keywords.zig",
|
|
"../test/behavior/export_keyword.zig",
|
|
"../test/behavior/export_self_referential_type_info.zig",
|
|
"../test/behavior/extern_struct_zero_size_fields.zig",
|
|
"../test/behavior/extern.zig",
|
|
"../test/behavior/fn_delegation.zig",
|
|
"../test/behavior/fn_in_struct_in_comptime.zig",
|
|
"../test/behavior/fn.zig",
|
|
"../test/behavior/for.zig",
|
|
"../test/behavior/generics.zig",
|
|
"../test/behavior/globals.zig",
|
|
"../test/behavior/hasdecl.zig",
|
|
"../test/behavior/hasfield.zig",
|
|
"../test/behavior/if.zig",
|
|
"../test/behavior/import_c_keywords.zig",
|
|
"../test/behavior/import.zig",
|
|
"../test/behavior/incomplete_struct_param_tld.zig",
|
|
"../test/behavior/inline_switch.zig",
|
|
"../test/behavior/int128.zig",
|
|
"../test/behavior/int_comparison_elision.zig",
|
|
"../test/behavior/ir_block_deps.zig",
|
|
"../test/behavior/lower_strlit_to_vector.zig",
|
|
"../test/behavior/maximum_minimum.zig",
|
|
"../test/behavior/member_func.zig",
|
|
"../test/behavior/memcpy.zig",
|
|
"../test/behavior/memmove.zig",
|
|
"../test/behavior/memset.zig",
|
|
"../test/behavior/merge_error_sets.zig",
|
|
"../test/behavior/muladd.zig",
|
|
"../test/behavior/multiple_externs_with_conflicting_types.zig",
|
|
"../test/behavior/namespace_depends_on_compile_var.zig",
|
|
"../test/behavior/nan.zig",
|
|
"../test/behavior/null.zig",
|
|
"../test/behavior/packed_struct_explicit_backing_int.zig",
|
|
"../test/behavior/packed-union.zig",
|
|
"../test/behavior/popcount.zig",
|
|
"../test/behavior/prefetch.zig",
|
|
"../test/behavior/ptrcast.zig",
|
|
"../test/behavior/ptrfromint.zig",
|
|
"../test/behavior/pub_enum.zig",
|
|
"../test/behavior/reflection.zig",
|
|
"../test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig",
|
|
"../test/behavior/return_address.zig",
|
|
"../test/behavior/saturating_arithmetic.zig",
|
|
"../test/behavior/select.zig",
|
|
"../test/behavior/shuffle.zig",
|
|
"../test/behavior/sizeof_and_typeof.zig",
|
|
"../test/behavior/slice_sentinel_comptime.zig",
|
|
"../test/behavior/src.zig",
|
|
"../test/behavior/string_literals.zig",
|
|
"../test/behavior/struct_contains_null_ptr_itself.zig",
|
|
"../test/behavior/struct_contains_slice_of_itself.zig",
|
|
"../test/behavior/switch_loop.zig",
|
|
"../test/behavior/switch_prong_err_enum.zig",
|
|
"../test/behavior/switch_prong_implicit_cast.zig",
|
|
"../test/behavior/this.zig",
|
|
"../test/behavior/threadlocal.zig",
|
|
"../test/behavior/truncate.zig",
|
|
"../test/behavior/try.zig",
|
|
"../test/behavior/tuple_declarations.zig",
|
|
"../test/behavior/tuple.zig",
|
|
"../test/behavior/type_info.zig",
|
|
"../test/behavior/typename.zig",
|
|
"../test/behavior/undefined.zig",
|
|
"../test/behavior/underscore.zig",
|
|
"../test/behavior/union_with_members.zig",
|
|
"../test/behavior/var_args.zig",
|
|
"../test/behavior/void.zig",
|
|
"../test/behavior/wasm.zig",
|
|
"../test/behavior/while.zig",
|
|
"../test/behavior/widening.zig",
|
|
"../test/behavior/wrapping_arithmetic.zig",
|
|
"../test/behavior/x86_64.zig",
|
|
"../test/behavior/zon.zig",
|
|
"../src/print_value.zig",
|
|
"../src/crash_report.zig",
|
|
"../src/target.zig",
|
|
"../src/link.zig",
|
|
"../src/Air.zig",
|
|
"../src/print_zir.zig",
|
|
"../src/Value.zig",
|
|
"../src/Type.zig",
|
|
"../src/translate_c.zig",
|
|
"../src/Compilation.zig",
|
|
"../src/InternPool.zig",
|
|
"../src/Sema.zig",
|
|
};
|
|
|
|
pub fn corpusCheck(gpa: Allocator, source: [:0]const u8) !void {
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
|
|
var tree = try parser_test.zigAst(gpa, c_ast);
|
|
defer tree.deinit(gpa);
|
|
var ref_zir = try AstGen.generate(gpa, tree);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
if (c_zir.has_compile_errors) {
|
|
std.debug.print("C port returned compile errors (inst_len={d})\n", .{c_zir.inst_len});
|
|
return error.TestUnexpectedResult;
|
|
}
|
|
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: struct single field" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const T = struct { x: u32 };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: struct multiple fields" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const T = struct { x: u32, y: bool };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: struct field with default" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const T = struct { x: u32 = 0 };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: struct field with align" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const T = struct { x: u32 align(4) };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: struct comptime field" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const T = struct { comptime x: u32 = 0 };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: empty error set" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const E = error{};";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: error set with members" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const E = error{ OutOfMemory, OutOfTime };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: extern var" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "extern var x: u32;";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: corpus test_all.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("test_all.zig"));
|
|
}
|
|
|
|
test "astgen: corpus build.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("../build.zig"));
|
|
}
|
|
|
|
test "astgen: corpus tokenizer_test.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("tokenizer_test.zig"));
|
|
}
|
|
|
|
test "astgen: corpus parser_test.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("parser_test.zig"));
|
|
}
|
|
|
|
test "astgen: corpus astgen_test.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("astgen_test.zig"));
|
|
}
|
|
|
|
test "astgen: corpus array_list.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("../lib/std/array_list.zig"));
|
|
}
|
|
|
|
test "astgen: corpus multi_array_list.zig" {
|
|
const gpa = std.testing.allocator;
|
|
try corpusCheck(gpa, @embedFile("../lib/std/multi_array_list.zig"));
|
|
}
|
|
|
|
test "astgen: enum decl" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 = "const E = enum { a, b, c };";
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: struct init typed" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const T = struct { x: u32 };
|
|
\\const v = T{ .x = 1 };
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: union with tag values" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const MultipleChoice = union(enum(u32)) {
|
|
\\ A = 20,
|
|
\\ B = 40,
|
|
\\ C = 60,
|
|
\\ D = 1000,
|
|
\\};
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: union with typed fields and tag values" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const MultipleChoice2 = union(enum(u32)) {
|
|
\\ Unspecified1: i32,
|
|
\\ A: f32 = 20,
|
|
\\ Unspecified2: void,
|
|
\\ B: bool = 40,
|
|
\\ Unspecified3: i32,
|
|
\\ C: i8 = 60,
|
|
\\ Unspecified4: void,
|
|
\\ D: void = 1000,
|
|
\\ Unspecified5: i32,
|
|
\\};
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: union with decl and var" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const U = union(enum) {
|
|
\\ a,
|
|
\\ b: u8,
|
|
\\ var u: @This() = .a;
|
|
\\};
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: union with fn" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const U = union(enum) {
|
|
\\ a,
|
|
\\ b: u8,
|
|
\\ fn doTest(c: bool) !void {
|
|
\\ const u = if (c) .a else @This(){ .b = 0 };
|
|
\\ _ = u;
|
|
\\ }
|
|
\\};
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: array if subscript" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const expect = @import("std").testing.expect;
|
|
\\test "type deduction for array subscript expression" {
|
|
\\ const S = struct {
|
|
\\ fn doTheTest() !void {
|
|
\\ var array = [_]u8{ 0x55, 0xAA };
|
|
\\ var v0 = true;
|
|
\\ try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]);
|
|
\\ var v1 = false;
|
|
\\ try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]);
|
|
\\ _ = .{ &array, &v0, &v1 };
|
|
\\ }
|
|
\\ };
|
|
\\ try S.doTheTest();
|
|
\\ try comptime S.doTheTest();
|
|
\\}
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: multi-arg TypeOf" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\test "peer type resolution" {
|
|
\\ var a: anyerror = error.Foo;
|
|
\\ var b: anyerror = error.Bar;
|
|
\\ const T = @TypeOf(a, b);
|
|
\\ _ = .{ T, &a, &b };
|
|
\\}
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: tagName in comptime" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\const E = enum { a, b };
|
|
\\fn f(v: E) []const u8 {
|
|
\\ return @tagName(v);
|
|
\\}
|
|
\\comptime {
|
|
\\ _ = f(.a);
|
|
\\}
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: 4-arg TypeOf" {
|
|
const gpa = std.testing.allocator;
|
|
const source: [:0]const u8 =
|
|
\\test "peer 4" {
|
|
\\ var a: [*:0]const u8 = "hi";
|
|
\\ var b: [*:0]volatile u8 = @constCast(@volatileCast(a));
|
|
\\ var cc: [*]allowzero const u8 = a;
|
|
\\ var d: [*]align(2) const u8 = @alignCast(a);
|
|
\\ comptime {
|
|
\\ _ = @TypeOf(a, b, cc, d);
|
|
\\ }
|
|
\\ _ = .{ &a, &b, &cc, &d };
|
|
\\}
|
|
;
|
|
var ref_zir = try refZir(gpa, source);
|
|
defer ref_zir.deinit(gpa);
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
|
}
|
|
|
|
test "astgen: corpus" {
|
|
// All individual corpus tests now pass.
|
|
const gpa = std.testing.allocator;
|
|
|
|
var any_fail = false;
|
|
inline for (corpus_files) |path| {
|
|
// std.debug.print("--- {s} ---\n", .{path});
|
|
corpusCheck(gpa, @embedFile(path)) catch {
|
|
std.debug.print("FAIL: {s}\n", .{path});
|
|
any_fail = true;
|
|
};
|
|
}
|
|
if (any_fail) return error.ZirMismatch;
|
|
}
|