| ... | ... | @@ -253,6 +253,79 @@ test "regular in irregular packed struct" { |
| 253 | 253 | try expectEqual(@as(u8, 42), foo.bar.b); |
| 254 | 254 | } |
| 255 | 255 | |
| 256 | test "nested packed struct unaligned" { |
| 257 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 258 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 259 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 260 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 261 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 262 | if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet |
| 263 | |
| 264 | const S1 = packed struct { |
| 265 | a: u4, |
| 266 | b: u4, |
| 267 | c: u8, |
| 268 | }; |
| 269 | const S2 = packed struct { |
| 270 | base: u8, |
| 271 | p0: S1, |
| 272 | bit0: u1, |
| 273 | p1: packed struct { |
| 274 | a: u8, |
| 275 | }, |
| 276 | p2: packed struct { |
| 277 | a: u7, |
| 278 | b: u8, |
| 279 | }, |
| 280 | p3: S1, |
| 281 | |
| 282 | var s: @This() = .{ |
| 283 | .base = 1, |
| 284 | .p0 = .{ .a = 2, .b = 3, .c = 4 }, |
| 285 | .bit0 = 0, |
| 286 | .p1 = .{ .a = 5 }, |
| 287 | .p2 = .{ .a = 6, .b = 7 }, |
| 288 | .p3 = .{ .a = 8, .b = 9, .c = 10 }, |
| 289 | }; |
| 290 | }; |
| 291 | |
| 292 | try expect(S2.s.base == 1); |
| 293 | try expect(S2.s.p0.a == 2); |
| 294 | try expect(S2.s.p0.b == 3); |
| 295 | try expect(S2.s.p0.c == 4); |
| 296 | try expect(S2.s.bit0 == 0); |
| 297 | try expect(S2.s.p1.a == 5); |
| 298 | try expect(S2.s.p2.a == 6); |
| 299 | try expect(S2.s.p2.b == 7); |
| 300 | try expect(S2.s.p3.a == 8); |
| 301 | try expect(S2.s.p3.b == 9); |
| 302 | try expect(S2.s.p3.c == 10); |
| 303 | |
| 304 | const S3 = packed struct { |
| 305 | pad: u8, |
| 306 | v: u2, |
| 307 | s: packed struct { |
| 308 | v: u3, |
| 309 | s: packed struct { |
| 310 | v: u2, |
| 311 | s: packed struct { |
| 312 | bit0: u1, |
| 313 | byte: u8, |
| 314 | bit1: u1, |
| 315 | }, |
| 316 | }, |
| 317 | }, |
| 318 | var v0: @This() = .{ .pad = 0, .v = 1, .s = .{ .v = 2, .s = .{ .v = 3, .s = .{ .bit0 = 0, .byte = 4, .bit1 = 1 } } } }; |
| 319 | }; |
| 320 | |
| 321 | try expect(S3.v0.v == 1); |
| 322 | try expect(S3.v0.s.v == 2); |
| 323 | try expect(S3.v0.s.s.v == 3); |
| 324 | try expect(S3.v0.s.s.s.bit0 == 0); |
| 325 | try expect(S3.v0.s.s.s.byte == 4); |
| 326 | try expect(S3.v0.s.s.s.bit1 == 1); |
| 327 | } |
| 328 | |
| 256 | 329 | test "byte-aligned field pointer offsets" { |
| 257 | 330 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 258 | 331 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | ... | @@ -354,6 +427,45 @@ test "byte-aligned field pointer offsets" { |
| 354 | 427 | try comptime S.doTheTest(); |
| 355 | 428 | } |
| 356 | 429 | |
| 430 | test "nested packed struct field pointers" { |
| 431 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 432 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 433 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 434 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 435 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access |
| 436 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 437 | if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet |
| 438 | |
| 439 | const S2 = packed struct { |
| 440 | base: u8, |
| 441 | p0: packed struct { |
| 442 | a: u4, |
| 443 | b: u4, |
| 444 | c: u8, |
| 445 | }, |
| 446 | bit: u1, |
| 447 | p1: packed struct { |
| 448 | a: u7, |
| 449 | b: u8, |
| 450 | }, |
| 451 | |
| 452 | var s: @This() = .{ .base = 1, .p0 = .{ .a = 2, .b = 3, .c = 4 }, .bit = 0, .p1 = .{ .a = 5, .b = 6 } }; |
| 453 | }; |
| 454 | |
| 455 | const ptr_base = &S2.s.base; |
| 456 | const ptr_p0_a = &S2.s.p0.a; |
| 457 | const ptr_p0_b = &S2.s.p0.b; |
| 458 | const ptr_p0_c = &S2.s.p0.c; |
| 459 | const ptr_p1_a = &S2.s.p1.a; |
| 460 | const ptr_p1_b = &S2.s.p1.b; |
| 461 | try expectEqual(@as(u8, 1), ptr_base.*); |
| 462 | try expectEqual(@as(u4, 2), ptr_p0_a.*); |
| 463 | try expectEqual(@as(u4, 3), ptr_p0_b.*); |
| 464 | try expectEqual(@as(u8, 4), ptr_p0_c.*); |
| 465 | try expectEqual(@as(u7, 5), ptr_p1_a.*); |
| 466 | try expectEqual(@as(u8, 6), ptr_p1_b.*); |
| 467 | } |
| 468 | |
| 357 | 469 | test "load pointer from packed struct" { |
| 358 | 470 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 359 | 471 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | ... | @@ -380,6 +492,7 @@ test "@intFromPtr on a packed struct field" { |
| 380 | 492 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 381 | 493 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 382 | 494 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 495 | if (native_endian != .Little) return error.SkipZigTest; |
| 383 | 496 | |
| 384 | 497 | const S = struct { |
| 385 | 498 | const P = packed struct { |
| ... | ... | @@ -387,6 +500,7 @@ test "@intFromPtr on a packed struct field" { |
| 387 | 500 | y: u8, |
| 388 | 501 | z: u32, |
| 389 | 502 | }; |
| 503 | |
| 390 | 504 | var p0: P = P{ |
| 391 | 505 | .x = 1, |
| 392 | 506 | .y = 2, |
| ... | ... | @@ -396,6 +510,140 @@ test "@intFromPtr on a packed struct field" { |
| 396 | 510 | try expect(@intFromPtr(&S.p0.z) - @intFromPtr(&S.p0.x) == 2); |
| 397 | 511 | } |
| 398 | 512 | |
| 513 | test "@intFromPtr on a packed struct field unaligned and nested" { |
| 514 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 515 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 516 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 517 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 518 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 519 | if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet |
| 520 | |
| 521 | const S1 = packed struct { |
| 522 | a: u4, |
| 523 | b: u4, |
| 524 | c: u8, |
| 525 | }; |
| 526 | const S2 = packed struct { |
| 527 | base: u8, |
| 528 | p0: S1, |
| 529 | bit0: u1, |
| 530 | p1: packed struct { |
| 531 | a: u8, |
| 532 | }, |
| 533 | p2: packed struct { |
| 534 | a: u7, |
| 535 | b: u8, |
| 536 | }, |
| 537 | p3: S1, |
| 538 | |
| 539 | var s: @This() = .{ |
| 540 | .base = 1, |
| 541 | .p0 = .{ .a = 2, .b = 3, .c = 4 }, |
| 542 | .bit0 = 0, |
| 543 | .p1 = .{ .a = 5 }, |
| 544 | .p2 = .{ .a = 6, .b = 7 }, |
| 545 | .p3 = .{ .a = 8, .b = 9, .c = 10 }, |
| 546 | }; |
| 547 | }; |
| 548 | |
| 549 | switch (comptime @alignOf(S2)) { |
| 550 | 4 => { |
| 551 | comptime assert(@TypeOf(&S2.s.base) == *align(4) u8); |
| 552 | comptime assert(@TypeOf(&S2.s.p0.a) == *align(1:0:2) u4); |
| 553 | comptime assert(@TypeOf(&S2.s.p0.b) == *align(1:4:2) u4); |
| 554 | comptime assert(@TypeOf(&S2.s.p0.c) == *u8); |
| 555 | comptime assert(@TypeOf(&S2.s.bit0) == *align(4:24:8) u1); |
| 556 | comptime assert(@TypeOf(&S2.s.p1.a) == *align(4:25:8) u8); |
| 557 | comptime assert(@TypeOf(&S2.s.p2.a) == *align(4:33:8) u7); |
| 558 | comptime assert(@TypeOf(&S2.s.p2.b) == *u8); |
| 559 | comptime assert(@TypeOf(&S2.s.p3.a) == *align(2:0:2) u4); |
| 560 | comptime assert(@TypeOf(&S2.s.p3.b) == *align(2:4:2) u4); |
| 561 | comptime assert(@TypeOf(&S2.s.p3.c) == *u8); |
| 562 | }, |
| 563 | 8 => { |
| 564 | comptime assert(@TypeOf(&S2.s.base) == *align(8) u8); |
| 565 | comptime assert(@TypeOf(&S2.s.p0.a) == *align(1:0:2) u4); |
| 566 | comptime assert(@TypeOf(&S2.s.p0.b) == *align(1:4:2) u4); |
| 567 | comptime assert(@TypeOf(&S2.s.p0.c) == *u8); |
| 568 | comptime assert(@TypeOf(&S2.s.bit0) == *align(8:24:8) u1); |
| 569 | comptime assert(@TypeOf(&S2.s.p1.a) == *align(8:25:8) u8); |
| 570 | comptime assert(@TypeOf(&S2.s.p2.a) == *align(8:33:8) u7); |
| 571 | comptime assert(@TypeOf(&S2.s.p2.b) == *u8); |
| 572 | comptime assert(@TypeOf(&S2.s.p3.a) == *align(2:0:2) u4); |
| 573 | comptime assert(@TypeOf(&S2.s.p3.b) == *align(2:4:2) u4); |
| 574 | comptime assert(@TypeOf(&S2.s.p3.c) == *u8); |
| 575 | }, |
| 576 | else => {}, |
| 577 | } |
| 578 | try expect(@intFromPtr(&S2.s.base) - @intFromPtr(&S2.s) == 0); |
| 579 | try expect(@intFromPtr(&S2.s.p0.a) - @intFromPtr(&S2.s) == 1); |
| 580 | try expect(@intFromPtr(&S2.s.p0.b) - @intFromPtr(&S2.s) == 1); |
| 581 | try expect(@intFromPtr(&S2.s.p0.c) - @intFromPtr(&S2.s) == 2); |
| 582 | try expect(@intFromPtr(&S2.s.bit0) - @intFromPtr(&S2.s) == 0); |
| 583 | try expect(@intFromPtr(&S2.s.p1.a) - @intFromPtr(&S2.s) == 0); |
| 584 | try expect(@intFromPtr(&S2.s.p2.a) - @intFromPtr(&S2.s) == 0); |
| 585 | try expect(@intFromPtr(&S2.s.p2.b) - @intFromPtr(&S2.s) == 5); |
| 586 | try expect(@intFromPtr(&S2.s.p3.a) - @intFromPtr(&S2.s) == 6); |
| 587 | try expect(@intFromPtr(&S2.s.p3.b) - @intFromPtr(&S2.s) == 6); |
| 588 | try expect(@intFromPtr(&S2.s.p3.c) - @intFromPtr(&S2.s) == 7); |
| 589 | |
| 590 | const S3 = packed struct { |
| 591 | pad: u8, |
| 592 | v: u2, |
| 593 | s: packed struct { |
| 594 | v: u3, |
| 595 | s: packed struct { |
| 596 | v: u2, |
| 597 | s: packed struct { |
| 598 | bit0: u1, |
| 599 | byte: u8, |
| 600 | bit1: u1, |
| 601 | }, |
| 602 | }, |
| 603 | }, |
| 604 | var v0: @This() = .{ .pad = 0, .v = 1, .s = .{ .v = 2, .s = .{ .v = 3, .s = .{ .bit0 = 0, .byte = 4, .bit1 = 1 } } } }; |
| 605 | }; |
| 606 | |
| 607 | comptime assert(@TypeOf(&S3.v0.v) == *align(4:8:4) u2); |
| 608 | comptime assert(@TypeOf(&S3.v0.s.v) == *align(4:10:4) u3); |
| 609 | comptime assert(@TypeOf(&S3.v0.s.s.v) == *align(4:13:4) u2); |
| 610 | comptime assert(@TypeOf(&S3.v0.s.s.s.bit0) == *align(4:15:4) u1); |
| 611 | comptime assert(@TypeOf(&S3.v0.s.s.s.byte) == *align(2) u8); |
| 612 | comptime assert(@TypeOf(&S3.v0.s.s.s.bit1) == *align(4:24:4) u1); |
| 613 | try expect(@intFromPtr(&S3.v0.v) - @intFromPtr(&S3.v0) == 0); |
| 614 | try expect(@intFromPtr(&S3.v0.s) - @intFromPtr(&S3.v0) == 0); |
| 615 | try expect(@intFromPtr(&S3.v0.s.v) - @intFromPtr(&S3.v0) == 0); |
| 616 | try expect(@intFromPtr(&S3.v0.s.s) - @intFromPtr(&S3.v0) == 0); |
| 617 | try expect(@intFromPtr(&S3.v0.s.s.v) - @intFromPtr(&S3.v0) == 0); |
| 618 | try expect(@intFromPtr(&S3.v0.s.s.s) - @intFromPtr(&S3.v0) == 0); |
| 619 | try expect(@intFromPtr(&S3.v0.s.s.s.bit0) - @intFromPtr(&S3.v0) == 0); |
| 620 | try expect(@intFromPtr(&S3.v0.s.s.s.byte) - @intFromPtr(&S3.v0) == 2); |
| 621 | try expect(@intFromPtr(&S3.v0.s.s.s.bit1) - @intFromPtr(&S3.v0) == 0); |
| 622 | } |
| 623 | |
| 624 | test "packed struct fields modification" { |
| 625 | // Originally reported at https://github.com/ziglang/zig/issues/16615 |
| 626 | |
| 627 | const Small = packed struct { |
| 628 | val: u8 = 0, |
| 629 | lo: u4 = 0, |
| 630 | hi: u4 = 0, |
| 631 | |
| 632 | var p: @This() = undefined; |
| 633 | }; |
| 634 | Small.p = .{ |
| 635 | .val = 0x12, |
| 636 | .lo = 3, |
| 637 | .hi = 4, |
| 638 | }; |
| 639 | try expect(@as(u16, @bitCast(Small.p)) == 0x4312); |
| 640 | |
| 641 | Small.p.val -= Small.p.lo; |
| 642 | Small.p.val += Small.p.hi; |
| 643 | Small.p.hi -= Small.p.lo; |
| 644 | try expect(@as(u16, @bitCast(Small.p)) == 0x1313); |
| 645 | } |
| 646 | |
| 399 | 647 | test "optional pointer in packed struct" { |
| 400 | 648 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 401 | 649 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | ... | @@ -410,7 +658,7 @@ test "optional pointer in packed struct" { |
| 410 | 658 | |
| 411 | 659 | test "nested packed struct field access test" { |
| 412 | 660 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 413 | | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 661 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits |
| 414 | 662 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 415 | 663 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 416 | 664 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -562,7 +810,7 @@ test "nested packed struct at non-zero offset 2" { |
| 562 | 810 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 563 | 811 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 564 | 812 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 565 | | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 813 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits |
| 566 | 814 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 567 | 815 | |
| 568 | 816 | const S = struct { |
| ... | ... | @@ -700,7 +948,6 @@ test "packed struct initialized in bitcast" { |
| 700 | 948 | test "pointer to container level packed struct field" { |
| 701 | 949 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 702 | 950 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 703 | | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 704 | 951 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 705 | 952 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 706 | 953 | |
| ... | ... | @@ -727,7 +974,6 @@ test "store undefined to packed result location" { |
| 727 | 974 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 728 | 975 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 729 | 976 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 730 | | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 731 | 977 | |
| 732 | 978 | var x: u4 = 0; |
| 733 | 979 | var s = packed struct { x: u4, y: u4 }{ .x = x, .y = if (x > 0) x else undefined }; |
| ... | ... | @@ -743,3 +989,51 @@ test "bitcast back and forth" { |
| 743 | 989 | try expect(s.one == s2.one); |
| 744 | 990 | try expect(s.two == s2.two); |
| 745 | 991 | } |
| 992 | |
| 993 | test "field access of packed struct smaller than its abi size inside struct initialized with rls" { |
| 994 | // Originally reported at https://github.com/ziglang/zig/issues/14200 |
| 995 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .arm) return error.SkipZigTest; |
| 996 | const S = struct { |
| 997 | ps: packed struct { x: i2, y: i2 }, |
| 998 | |
| 999 | fn init(cond: bool) @This() { |
| 1000 | return .{ .ps = .{ .x = 0, .y = if (cond) 1 else 0 } }; |
| 1001 | } |
| 1002 | }; |
| 1003 | |
| 1004 | var s = S.init(true); |
| 1005 | // note: this bug is triggered by the == operator, expectEqual will hide it |
| 1006 | try expect(@as(i2, 0) == s.ps.x); |
| 1007 | try expect(@as(i2, 1) == s.ps.y); |
| 1008 | } |
| 1009 | |
| 1010 | test "modify nested packed struct aligned field" { |
| 1011 | // Originally reported at https://github.com/ziglang/zig/issues/14632 |
| 1012 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1013 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 1014 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 1015 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 1016 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 1017 | |
| 1018 | const Options = packed struct { |
| 1019 | foo: bool = false, |
| 1020 | bar: bool = false, |
| 1021 | pretty_print: packed struct { |
| 1022 | enabled: bool = false, |
| 1023 | num_spaces: u4 = 4, |
| 1024 | space_char: enum { space, tab } = .space, |
| 1025 | indent: u8 = 0, |
| 1026 | } = .{}, |
| 1027 | baz: bool = false, |
| 1028 | }; |
| 1029 | |
| 1030 | var opts = Options{}; |
| 1031 | opts.pretty_print.indent += 1; |
| 1032 | try std.testing.expectEqual(@as(u17, 0b00000000100100000), @bitCast(opts)); |
| 1033 | try std.testing.expect(!opts.foo); |
| 1034 | try std.testing.expect(!opts.bar); |
| 1035 | try std.testing.expect(!opts.pretty_print.enabled); |
| 1036 | try std.testing.expectEqual(@as(u4, 4), opts.pretty_print.num_spaces); |
| 1037 | try std.testing.expectEqual(@as(u8, 1), opts.pretty_print.indent); |
| 1038 | try std.testing.expect(!opts.baz); |
| 1039 | } |