astgen_test.zig (29707B) - Raw
1 const std = @import("std"); 2 const Ast = std.zig.Ast; 3 const Zir = std.zig.Zir; 4 const AstGen = std.zig.AstGen; 5 const Allocator = std.mem.Allocator; 6 7 const c = @cImport({ 8 @cInclude("astgen.h"); 9 }); 10 11 fn refZir(gpa: Allocator, source: [:0]const u8) !Zir { 12 var tree = try Ast.parse(gpa, source, .zig); 13 defer tree.deinit(gpa); 14 return try AstGen.generate(gpa, tree); 15 } 16 17 test "astgen dump: simple cases" { 18 const gpa = std.testing.allocator; 19 20 const cases = .{ 21 .{ "empty", "" }, 22 .{ "comptime {}", "comptime {}" }, 23 .{ "const x = 0;", "const x = 0;" }, 24 .{ "const x = 1;", "const x = 1;" }, 25 .{ "const x = 0; const y = 0;", "const x = 0; const y = 0;" }, 26 .{ "test \"t\" {}", "test \"t\" {}" }, 27 .{ "const std = @import(\"std\");", "const std = @import(\"std\");" }, 28 .{ "test_all.zig", @embedFile("test_all.zig") }, 29 }; 30 31 inline for (cases) |case| { 32 // std.debug.print("--- {s} ---\n", .{case[0]}); 33 const source: [:0]const u8 = case[1]; 34 var zir = try refZir(gpa, source); 35 zir.deinit(gpa); 36 } 37 } 38 39 /// Build a mask of extra[] indices that contain hash data (src_hash or 40 /// fields_hash). These are zero-filled in the C output but contain real 41 /// Blake3 hashes in the Zig reference. We skip these positions during 42 /// comparison. 43 fn buildHashSkipMask(gpa: Allocator, ref: Zir) ![]bool { 44 const ref_extra_len: u32 = @intCast(ref.extra.len); 45 const skip = try gpa.alloc(bool, ref_extra_len); 46 @memset(skip, false); 47 48 const ref_len: u32 = @intCast(ref.instructions.len); 49 const ref_tags = ref.instructions.items(.tag); 50 const ref_datas = ref.instructions.items(.data); 51 for (0..ref_len) |i| { 52 switch (ref_tags[i]) { 53 .extended => { 54 const ext = ref_datas[i].extended; 55 if (ext.opcode == .struct_decl or ext.opcode == .enum_decl) { 56 // StructDecl/EnumDecl starts with fields_hash[4]. 57 const pi = ext.operand; 58 for (0..4) |j| skip[pi + j] = true; 59 } 60 }, 61 .declaration => { 62 // Declaration starts with src_hash[4]. 63 const pi = ref_datas[i].declaration.payload_index; 64 for (0..4) |j| skip[pi + j] = true; 65 }, 66 .func, .func_inferred => { 67 // Func payload: ret_ty(1) + param_block(1) + body_len(1) 68 // + trailing ret_ty + body + SrcLocs(3) + proto_hash(4). 69 const pi = ref_datas[i].pl_node.payload_index; 70 const ret_ty_raw: u32 = ref.extra[pi]; 71 const ret_body_len: u32 = ret_ty_raw & 0x7FFFFFFF; 72 const body_len: u32 = ref.extra[pi + 2]; 73 // ret_ty trailing: if body_len > 1, it's a body; if == 1, it's a ref; if 0, void. 74 const ret_trailing: u32 = if (ret_body_len > 1) ret_body_len else if (ret_body_len == 1) 1 else 0; 75 // proto_hash is at: pi + 3 + ret_trailing + body_len + 3 76 if (body_len > 0) { 77 const hash_start = pi + 3 + ret_trailing + body_len + 3; 78 for (0..4) |j| { 79 if (hash_start + j < ref_extra_len) 80 skip[hash_start + j] = true; 81 } 82 } 83 }, 84 else => {}, 85 } 86 } 87 return skip; 88 } 89 90 test "astgen: empty source" { 91 const gpa = std.testing.allocator; 92 const source: [:0]const u8 = ""; 93 94 var ref_zir = try refZir(gpa, source); 95 defer ref_zir.deinit(gpa); 96 97 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 98 defer c.astDeinit(&c_ast); 99 var c_zir = c.astGen(&c_ast); 100 defer c.zirDeinit(&c_zir); 101 102 try expectEqualZir(gpa, ref_zir, c_zir); 103 } 104 105 test "astgen: comptime {}" { 106 const gpa = std.testing.allocator; 107 const source: [:0]const u8 = "comptime {}"; 108 109 var ref_zir = try refZir(gpa, source); 110 defer ref_zir.deinit(gpa); 111 112 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 113 defer c.astDeinit(&c_ast); 114 var c_zir = c.astGen(&c_ast); 115 defer c.zirDeinit(&c_zir); 116 117 try expectEqualZir(gpa, ref_zir, c_zir); 118 } 119 120 test "astgen: const x = 0;" { 121 const gpa = std.testing.allocator; 122 const source: [:0]const u8 = "const x = 0;"; 123 124 var ref_zir = try refZir(gpa, source); 125 defer ref_zir.deinit(gpa); 126 127 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 128 defer c.astDeinit(&c_ast); 129 var c_zir = c.astGen(&c_ast); 130 defer c.zirDeinit(&c_zir); 131 132 try expectEqualZir(gpa, ref_zir, c_zir); 133 } 134 135 test "astgen: const x = 1;" { 136 const gpa = std.testing.allocator; 137 const source: [:0]const u8 = "const x = 1;"; 138 139 var ref_zir = try refZir(gpa, source); 140 defer ref_zir.deinit(gpa); 141 142 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 143 defer c.astDeinit(&c_ast); 144 var c_zir = c.astGen(&c_ast); 145 defer c.zirDeinit(&c_zir); 146 147 try expectEqualZir(gpa, ref_zir, c_zir); 148 } 149 150 test "astgen: const x = 0; const y = 0;" { 151 const gpa = std.testing.allocator; 152 const source: [:0]const u8 = "const x = 0; const y = 0;"; 153 154 var ref_zir = try refZir(gpa, source); 155 defer ref_zir.deinit(gpa); 156 157 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 158 defer c.astDeinit(&c_ast); 159 var c_zir = c.astGen(&c_ast); 160 defer c.zirDeinit(&c_zir); 161 162 try expectEqualZir(gpa, ref_zir, c_zir); 163 } 164 165 test "astgen: field_access" { 166 const gpa = std.testing.allocator; 167 const source: [:0]const u8 = "const std = @import(\"std\");\nconst mem = std.mem;"; 168 169 var ref_zir = try refZir(gpa, source); 170 defer ref_zir.deinit(gpa); 171 172 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 173 defer c.astDeinit(&c_ast); 174 var c_zir = c.astGen(&c_ast); 175 defer c.zirDeinit(&c_zir); 176 177 try expectEqualZir(gpa, ref_zir, c_zir); 178 } 179 180 test "astgen: addr array init" { 181 const gpa = std.testing.allocator; 182 const source: [:0]const u8 = "const x = &[_][]const u8{\"a\",\"b\"};"; 183 184 var ref_zir = try refZir(gpa, source); 185 defer ref_zir.deinit(gpa); 186 187 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 188 defer c.astDeinit(&c_ast); 189 var c_zir = c.astGen(&c_ast); 190 defer c.zirDeinit(&c_zir); 191 192 try expectEqualZir(gpa, ref_zir, c_zir); 193 } 194 195 test "astgen: test empty body" { 196 const gpa = std.testing.allocator; 197 const source: [:0]const u8 = "test \"t\" {}"; 198 199 var ref_zir = try refZir(gpa, source); 200 defer ref_zir.deinit(gpa); 201 202 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 203 defer c.astDeinit(&c_ast); 204 var c_zir = c.astGen(&c_ast); 205 defer c.zirDeinit(&c_zir); 206 207 try expectEqualZir(gpa, ref_zir, c_zir); 208 } 209 210 test "astgen: test_all.zig" { 211 const gpa = std.testing.allocator; 212 const source: [:0]const u8 = @embedFile("test_all.zig"); 213 214 var ref_zir = try refZir(gpa, source); 215 defer ref_zir.deinit(gpa); 216 217 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 218 defer c.astDeinit(&c_ast); 219 var c_zir = c.astGen(&c_ast); 220 defer c.zirDeinit(&c_zir); 221 222 try expectEqualZir(gpa, ref_zir, c_zir); 223 } 224 225 test "astgen: @import" { 226 const gpa = std.testing.allocator; 227 const source: [:0]const u8 = "const std = @import(\"std\");"; 228 229 var ref_zir = try refZir(gpa, source); 230 defer ref_zir.deinit(gpa); 231 232 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 233 defer c.astDeinit(&c_ast); 234 var c_zir = c.astGen(&c_ast); 235 defer c.zirDeinit(&c_zir); 236 237 try expectEqualZir(gpa, ref_zir, c_zir); 238 } 239 240 fn expectEqualZir(gpa: Allocator, ref: Zir, got: c.Zir) !void { 241 const ref_len: u32 = @intCast(ref.instructions.len); 242 const ref_tags = ref.instructions.items(.tag); 243 const ref_datas = ref.instructions.items(.data); 244 245 // 1. Compare lengths. 246 try std.testing.expectEqual(ref_len, got.inst_len); 247 248 // 2. Compare instruction tags. 249 for (0..ref_len) |i| { 250 const ref_tag: u8 = @intFromEnum(ref_tags[i]); 251 const got_tag: u8 = @intCast(got.inst_tags[i]); 252 if (ref_tag != got_tag) { 253 std.debug.print( 254 "inst_tags[{d}] mismatch: ref={d} got={d}\n", 255 .{ i, ref_tag, got_tag }, 256 ); 257 return error.TestExpectedEqual; 258 } 259 } 260 261 // 3. Compare instruction data field-by-field. 262 for (0..ref_len) |i| { 263 try expectEqualData(i, ref_tags[i], ref_datas[i], got.inst_datas[i]); 264 } 265 // 4. Compare string bytes. 266 const ref_sb_len: u32 = @intCast(ref.string_bytes.len); 267 try std.testing.expectEqual(ref_sb_len, got.string_bytes_len); 268 for (0..ref_sb_len) |i| { 269 if (ref.string_bytes[i] != got.string_bytes[i]) { 270 std.debug.print( 271 "string_bytes[{d}] mismatch: ref=0x{x:0>2} got=0x{x:0>2}\n", 272 .{ i, ref.string_bytes[i], got.string_bytes[i] }, 273 ); 274 return error.TestExpectedEqual; 275 } 276 } 277 278 // 5. Compare extra data (skipping hash positions). 279 const skip = try buildHashSkipMask(gpa, ref); 280 defer gpa.free(skip); 281 const ref_extra_len: u32 = @intCast(ref.extra.len); 282 try std.testing.expectEqual(ref_extra_len, got.extra_len); 283 for (0..ref_extra_len) |i| { 284 if (skip[i]) continue; 285 if (ref.extra[i] != got.extra[i]) { 286 // Show first 10 extra diffs. 287 var count: u32 = 0; 288 for (0..ref_extra_len) |j| { 289 if (!skip[j] and ref.extra[j] != got.extra[j]) { 290 std.debug.print( 291 "extra[{d}] mismatch: ref={d} got={d}\n", 292 .{ j, ref.extra[j], got.extra[j] }, 293 ); 294 count += 1; 295 if (count >= 10) break; 296 } 297 } 298 return error.TestExpectedEqual; 299 } 300 } 301 } 302 303 /// Compare a single instruction's data, dispatching by tag. 304 /// Zig's Data union has no guaranteed in-memory layout, so we 305 /// compare each variant's fields individually. 306 fn expectEqualData( 307 idx: usize, 308 tag: Zir.Inst.Tag, 309 ref: Zir.Inst.Data, 310 got: c.ZirInstData, 311 ) !void { 312 switch (tag) { 313 .extended => { 314 const r = ref.extended; 315 const g = got.extended; 316 // Some extended opcodes have undefined/unused small+operand. 317 const skip_data = switch (r.opcode) { 318 .dbg_empty_stmt, .astgen_error => true, 319 else => false, 320 }; 321 const skip_small = switch (r.opcode) { 322 .add_with_overflow, 323 .sub_with_overflow, 324 .mul_with_overflow, 325 .shl_with_overflow, 326 .restore_err_ret_index, 327 .branch_hint, 328 => true, 329 else => false, 330 }; 331 if (@intFromEnum(r.opcode) != g.opcode or 332 (!skip_data and !skip_small and r.small != g.small) or 333 (!skip_data and r.operand != g.operand)) 334 { 335 std.debug.print( 336 "inst_datas[{d}] (extended) mismatch:\n" ++ 337 " ref: opcode={d} small=0x{x:0>4} operand={d}\n" ++ 338 " got: opcode={d} small=0x{x:0>4} operand={d}\n", 339 .{ 340 idx, 341 @intFromEnum(r.opcode), 342 r.small, 343 r.operand, 344 g.opcode, 345 g.small, 346 g.operand, 347 }, 348 ); 349 return error.TestExpectedEqual; 350 } 351 }, 352 .declaration => { 353 const r = ref.declaration; 354 const g = got.declaration; 355 if (@intFromEnum(r.src_node) != g.src_node or 356 r.payload_index != g.payload_index) 357 { 358 std.debug.print( 359 "inst_datas[{d}] (declaration) mismatch:\n" ++ 360 " ref: src_node={d} payload_index={d}\n" ++ 361 " got: src_node={d} payload_index={d}\n", 362 .{ 363 idx, 364 @intFromEnum(r.src_node), 365 r.payload_index, 366 g.src_node, 367 g.payload_index, 368 }, 369 ); 370 return error.TestExpectedEqual; 371 } 372 }, 373 .break_inline => { 374 const r = ref.@"break"; 375 const g = got.break_data; 376 if (@intFromEnum(r.operand) != g.operand or 377 r.payload_index != g.payload_index) 378 { 379 std.debug.print( 380 "inst_datas[{d}] (break_inline) mismatch:\n" ++ 381 " ref: operand={d} payload_index={d}\n" ++ 382 " got: operand={d} payload_index={d}\n", 383 .{ 384 idx, 385 @intFromEnum(r.operand), 386 r.payload_index, 387 g.operand, 388 g.payload_index, 389 }, 390 ); 391 return error.TestExpectedEqual; 392 } 393 }, 394 .import => { 395 const r = ref.pl_tok; 396 const g = got.pl_tok; 397 if (@intFromEnum(r.src_tok) != g.src_tok or 398 r.payload_index != g.payload_index) 399 { 400 std.debug.print( 401 "inst_datas[{d}] (import) mismatch:\n" ++ 402 " ref: src_tok={d} payload_index={d}\n" ++ 403 " got: src_tok={d} payload_index={d}\n", 404 .{ 405 idx, 406 @intFromEnum(r.src_tok), 407 r.payload_index, 408 g.src_tok, 409 g.payload_index, 410 }, 411 ); 412 return error.TestExpectedEqual; 413 } 414 }, 415 .dbg_stmt => { 416 const r = ref.dbg_stmt; 417 const g = got.dbg_stmt; 418 if (r.line != g.line or r.column != g.column) { 419 std.debug.print( 420 "inst_datas[{d}] (dbg_stmt) mismatch:\n" ++ 421 " ref: line={d} column={d}\n" ++ 422 " got: line={d} column={d}\n", 423 .{ idx, r.line, r.column, g.line, g.column }, 424 ); 425 return error.TestExpectedEqual; 426 } 427 }, 428 .ensure_result_non_error, 429 .restore_err_ret_index_unconditional, 430 .validate_struct_init_ty, 431 .validate_struct_init_result_ty, 432 .struct_init_empty_result, 433 .struct_init_empty, 434 .struct_init_empty_ref_result, 435 => { 436 const r = ref.un_node; 437 const g = got.un_node; 438 if (@intFromEnum(r.src_node) != g.src_node or 439 @intFromEnum(r.operand) != g.operand) 440 { 441 std.debug.print( 442 "inst_datas[{d}] ({s}) mismatch:\n" ++ 443 " ref: src_node={d} operand={d}\n" ++ 444 " got: src_node={d} operand={d}\n", 445 .{ 446 idx, 447 @tagName(tag), 448 @intFromEnum(r.src_node), 449 @intFromEnum(r.operand), 450 g.src_node, 451 g.operand, 452 }, 453 ); 454 return error.TestExpectedEqual; 455 } 456 }, 457 .ret_implicit => { 458 const r = ref.un_tok; 459 const g = got.un_tok; 460 if (@intFromEnum(r.src_tok) != g.src_tok or 461 @intFromEnum(r.operand) != g.operand) 462 { 463 std.debug.print( 464 "inst_datas[{d}] (ret_implicit) mismatch:\n" ++ 465 " ref: src_tok={d} operand={d}\n" ++ 466 " got: src_tok={d} operand={d}\n", 467 .{ 468 idx, 469 @intFromEnum(r.src_tok), 470 @intFromEnum(r.operand), 471 g.src_tok, 472 g.operand, 473 }, 474 ); 475 return error.TestExpectedEqual; 476 } 477 }, 478 .func, 479 .func_inferred, 480 .array_type, 481 .array_type_sentinel, 482 .array_cat, 483 .array_init, 484 .array_init_ref, 485 .error_set_decl, 486 .struct_init_field_type, 487 .struct_init, 488 .struct_init_ref, 489 .validate_array_init_ref_ty, 490 .validate_array_init_ty, 491 => { 492 const r = ref.pl_node; 493 const g = got.pl_node; 494 if (@intFromEnum(r.src_node) != g.src_node or 495 r.payload_index != g.payload_index) 496 { 497 std.debug.print( 498 "inst_datas[{d}] ({s}) mismatch:\n" ++ 499 " ref: src_node={d} payload_index={d}\n" ++ 500 " got: src_node={d} payload_index={d}\n", 501 .{ 502 idx, 503 @tagName(tag), 504 @intFromEnum(r.src_node), 505 r.payload_index, 506 g.src_node, 507 g.payload_index, 508 }, 509 ); 510 return error.TestExpectedEqual; 511 } 512 }, 513 .decl_val, .decl_ref => { 514 const r = ref.str_tok; 515 const g = got.str_tok; 516 if (@intFromEnum(r.start) != g.start or @intFromEnum(r.src_tok) != g.src_tok) { 517 std.debug.print( 518 "inst_datas[{d}] ({s}) mismatch:\n" ++ 519 " ref: start={d} src_tok={d}\n" ++ 520 " got: start={d} src_tok={d}\n", 521 .{ 522 idx, 523 @tagName(tag), 524 @intFromEnum(r.start), 525 @intFromEnum(r.src_tok), 526 g.start, 527 g.src_tok, 528 }, 529 ); 530 return error.TestExpectedEqual; 531 } 532 }, 533 .field_val, .field_ptr, .field_val_named, .field_ptr_named => { 534 const r = ref.pl_node; 535 const g = got.pl_node; 536 if (@intFromEnum(r.src_node) != g.src_node or 537 r.payload_index != g.payload_index) 538 { 539 std.debug.print( 540 "inst_datas[{d}] ({s}) mismatch:\n" ++ 541 " ref: src_node={d} payload_index={d}\n" ++ 542 " got: src_node={d} payload_index={d}\n", 543 .{ 544 idx, 545 @tagName(tag), 546 @intFromEnum(r.src_node), 547 r.payload_index, 548 g.src_node, 549 g.payload_index, 550 }, 551 ); 552 return error.TestExpectedEqual; 553 } 554 }, 555 .int => { 556 if (ref.int != got.int_val) { 557 std.debug.print( 558 "inst_datas[{d}] (int) mismatch: ref={d} got={d}\n", 559 .{ idx, ref.int, got.int_val }, 560 ); 561 return error.TestExpectedEqual; 562 } 563 }, 564 .ptr_type => { 565 // Compare ptr_type data: flags, size, payload_index. 566 if (@as(u8, @bitCast(ref.ptr_type.flags)) != got.ptr_type.flags or 567 @intFromEnum(ref.ptr_type.size) != got.ptr_type.size or 568 ref.ptr_type.payload_index != got.ptr_type.payload_index) 569 { 570 std.debug.print( 571 "inst_datas[{d}] (ptr_type) mismatch:\n" ++ 572 " ref: flags=0x{x} size={d} pi={d}\n" ++ 573 " got: flags=0x{x} size={d} pi={d}\n", 574 .{ 575 idx, 576 @as(u8, @bitCast(ref.ptr_type.flags)), 577 @intFromEnum(ref.ptr_type.size), 578 ref.ptr_type.payload_index, 579 got.ptr_type.flags, 580 got.ptr_type.size, 581 got.ptr_type.payload_index, 582 }, 583 ); 584 return error.TestExpectedEqual; 585 } 586 }, 587 .int_type => { 588 const r = ref.int_type; 589 const g = got.int_type; 590 if (@intFromEnum(r.src_node) != g.src_node or 591 @intFromEnum(r.signedness) != g.signedness or 592 r.bit_count != g.bit_count) 593 { 594 std.debug.print( 595 "inst_datas[{d}] (int_type) mismatch\n", 596 .{idx}, 597 ); 598 return error.TestExpectedEqual; 599 } 600 }, 601 .str => { 602 const r = ref.str; 603 const g = got.str; 604 if (@intFromEnum(r.start) != g.start or r.len != g.len) { 605 std.debug.print( 606 "inst_datas[{d}] (str) mismatch:\n" ++ 607 " ref: start={d} len={d}\n" ++ 608 " got: start={d} len={d}\n", 609 .{ idx, @intFromEnum(r.start), r.len, g.start, g.len }, 610 ); 611 return error.TestExpectedEqual; 612 } 613 }, 614 else => { 615 // Generic raw comparison: treat data as two u32 words. 616 // Tags using .node data format have undefined second word. 617 const ref_raw = @as([*]const u32, @ptrCast(&ref)); 618 const got_raw = @as([*]const u32, @ptrCast(&got)); 619 // Tags where only the first u32 word is meaningful 620 // (second word is padding/undefined). 621 const first_word_only = switch (tag) { 622 // .node data format (single i32): 623 .repeat, 624 .repeat_inline, 625 .ret_ptr, 626 .ret_type, 627 .trap, 628 .alloc_inferred, 629 .alloc_inferred_mut, 630 .alloc_inferred_comptime, 631 .alloc_inferred_comptime_mut, 632 // .@"unreachable" data format (src_node + padding): 633 .@"unreachable", 634 // .save_err_ret_index data format (operand only): 635 .save_err_ret_index, 636 => true, 637 else => false, 638 }; 639 const w1_match = ref_raw[0] == got_raw[0]; 640 const w2_match = first_word_only or ref_raw[1] == got_raw[1]; 641 if (!w1_match or !w2_match) { 642 std.debug.print( 643 "inst_datas[{d}] ({s}) raw mismatch:\n" ++ 644 " ref: 0x{x:0>8} 0x{x:0>8}\n" ++ 645 " got: 0x{x:0>8} 0x{x:0>8}\n", 646 .{ 647 idx, 648 @tagName(tag), 649 ref_raw[0], 650 ref_raw[1], 651 got_raw[0], 652 got_raw[1], 653 }, 654 ); 655 return error.TestExpectedEqual; 656 } 657 }, 658 } 659 } 660 661 const corpus_files = .{ 662 .{ "astgen_test.zig", @embedFile("astgen_test.zig") }, 663 .{ "build.zig", @embedFile("build.zig") }, 664 .{ "parser_test.zig", @embedFile("parser_test.zig") }, 665 .{ "test_all.zig", @embedFile("test_all.zig") }, 666 .{ "tokenizer_test.zig", @embedFile("tokenizer_test.zig") }, 667 }; 668 669 fn corpusCheck(gpa: Allocator, source: [:0]const u8) !void { 670 var tree = try Ast.parse(gpa, source, .zig); 671 defer tree.deinit(gpa); 672 673 var ref_zir = try AstGen.generate(gpa, tree); 674 defer ref_zir.deinit(gpa); 675 676 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 677 defer c.astDeinit(&c_ast); 678 var c_zir = c.astGen(&c_ast); 679 defer c.zirDeinit(&c_zir); 680 681 if (c_zir.has_compile_errors) { 682 std.debug.print("C port returned compile errors (inst_len={d})\n", .{c_zir.inst_len}); 683 return error.TestUnexpectedResult; 684 } 685 686 try expectEqualZir(gpa, ref_zir, c_zir); 687 } 688 689 test "astgen: struct single field" { 690 const gpa = std.testing.allocator; 691 const source: [:0]const u8 = "const T = struct { x: u32 };"; 692 var ref_zir = try refZir(gpa, source); 693 defer ref_zir.deinit(gpa); 694 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 695 defer c.astDeinit(&c_ast); 696 var c_zir = c.astGen(&c_ast); 697 defer c.zirDeinit(&c_zir); 698 try expectEqualZir(gpa, ref_zir, c_zir); 699 } 700 701 test "astgen: struct multiple fields" { 702 const gpa = std.testing.allocator; 703 const source: [:0]const u8 = "const T = struct { x: u32, y: bool };"; 704 var ref_zir = try refZir(gpa, source); 705 defer ref_zir.deinit(gpa); 706 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 707 defer c.astDeinit(&c_ast); 708 var c_zir = c.astGen(&c_ast); 709 defer c.zirDeinit(&c_zir); 710 try expectEqualZir(gpa, ref_zir, c_zir); 711 } 712 713 test "astgen: struct field with default" { 714 const gpa = std.testing.allocator; 715 const source: [:0]const u8 = "const T = struct { x: u32 = 0 };"; 716 var ref_zir = try refZir(gpa, source); 717 defer ref_zir.deinit(gpa); 718 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 719 defer c.astDeinit(&c_ast); 720 var c_zir = c.astGen(&c_ast); 721 defer c.zirDeinit(&c_zir); 722 try expectEqualZir(gpa, ref_zir, c_zir); 723 } 724 725 test "astgen: struct field with align" { 726 const gpa = std.testing.allocator; 727 const source: [:0]const u8 = "const T = struct { x: u32 align(4) };"; 728 var ref_zir = try refZir(gpa, source); 729 defer ref_zir.deinit(gpa); 730 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 731 defer c.astDeinit(&c_ast); 732 var c_zir = c.astGen(&c_ast); 733 defer c.zirDeinit(&c_zir); 734 try expectEqualZir(gpa, ref_zir, c_zir); 735 } 736 737 test "astgen: struct comptime field" { 738 const gpa = std.testing.allocator; 739 const source: [:0]const u8 = "const T = struct { comptime x: u32 = 0 };"; 740 var ref_zir = try refZir(gpa, source); 741 defer ref_zir.deinit(gpa); 742 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 743 defer c.astDeinit(&c_ast); 744 var c_zir = c.astGen(&c_ast); 745 defer c.zirDeinit(&c_zir); 746 try expectEqualZir(gpa, ref_zir, c_zir); 747 } 748 749 test "astgen: empty error set" { 750 const gpa = std.testing.allocator; 751 const source: [:0]const u8 = "const E = error{};"; 752 var ref_zir = try refZir(gpa, source); 753 defer ref_zir.deinit(gpa); 754 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 755 defer c.astDeinit(&c_ast); 756 var c_zir = c.astGen(&c_ast); 757 defer c.zirDeinit(&c_zir); 758 try expectEqualZir(gpa, ref_zir, c_zir); 759 } 760 761 test "astgen: error set with members" { 762 const gpa = std.testing.allocator; 763 const source: [:0]const u8 = "const E = error{ OutOfMemory, OutOfTime };"; 764 var ref_zir = try refZir(gpa, source); 765 defer ref_zir.deinit(gpa); 766 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 767 defer c.astDeinit(&c_ast); 768 var c_zir = c.astGen(&c_ast); 769 defer c.zirDeinit(&c_zir); 770 try expectEqualZir(gpa, ref_zir, c_zir); 771 } 772 773 test "astgen: extern var" { 774 const gpa = std.testing.allocator; 775 const source: [:0]const u8 = "extern var x: u32;"; 776 var ref_zir = try refZir(gpa, source); 777 defer ref_zir.deinit(gpa); 778 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 779 defer c.astDeinit(&c_ast); 780 var c_zir = c.astGen(&c_ast); 781 defer c.zirDeinit(&c_zir); 782 try expectEqualZir(gpa, ref_zir, c_zir); 783 } 784 785 test "astgen: corpus test_all.zig" { 786 const gpa = std.testing.allocator; 787 try corpusCheck(gpa, @embedFile("test_all.zig")); 788 } 789 790 test "astgen: corpus build.zig" { 791 const gpa = std.testing.allocator; 792 try corpusCheck(gpa, @embedFile("build.zig")); 793 } 794 795 test "astgen: corpus tokenizer_test.zig" { 796 const gpa = std.testing.allocator; 797 try corpusCheck(gpa, @embedFile("tokenizer_test.zig")); 798 } 799 800 test "astgen: corpus parser_test.zig" { 801 // TODO: 10+ extra data mismatches (ref=48 got=32, bit 4 = propagate_error_trace) 802 // in call instruction flags — ctx propagation differs from upstream. 803 if (true) return error.SkipZigTest; 804 const gpa = std.testing.allocator; 805 try corpusCheck(gpa, @embedFile("parser_test.zig")); 806 } 807 808 test "astgen: corpus astgen_test.zig" { 809 const gpa = std.testing.allocator; 810 try corpusCheck(gpa, @embedFile("astgen_test.zig")); 811 } 812 813 test "astgen: enum decl" { 814 const gpa = std.testing.allocator; 815 const source: [:0]const u8 = "const E = enum { a, b, c };"; 816 var ref_zir = try refZir(gpa, source); 817 defer ref_zir.deinit(gpa); 818 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 819 defer c.astDeinit(&c_ast); 820 var c_zir = c.astGen(&c_ast); 821 defer c.zirDeinit(&c_zir); 822 try expectEqualZir(gpa, ref_zir, c_zir); 823 } 824 825 test "astgen: struct init typed" { 826 const gpa = std.testing.allocator; 827 const source: [:0]const u8 = 828 \\const T = struct { x: u32 }; 829 \\const v = T{ .x = 1 }; 830 ; 831 var ref_zir = try refZir(gpa, source); 832 defer ref_zir.deinit(gpa); 833 var c_ast = c.astParse(source.ptr, @intCast(source.len)); 834 defer c.astDeinit(&c_ast); 835 var c_zir = c.astGen(&c_ast); 836 defer c.zirDeinit(&c_zir); 837 try expectEqualZir(gpa, ref_zir, c_zir); 838 } 839 840 test "astgen: corpus" { 841 if (true) return error.SkipZigTest; // TODO: parser_test.zig fails 842 const gpa = std.testing.allocator; 843 844 var any_fail = false; 845 inline for (corpus_files) |entry| { 846 corpusCheck(gpa, entry[1]) catch { 847 any_fail = true; 848 }; 849 } 850 if (any_fail) return error.ZirMismatch; 851 }