authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-10 15:04:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-10 15:04:39-07:00
log3c3bc5af29a5fcd1daaf8d8d39625c7b505e80bf
tree265b5cfeb0847a23439a58e8c54e31bf4bb2a615
parent58bc562cb45d5d27fed2e69ee289a4b5c9199cff

Sema: introduce bitSizeAdvanced to recursively resolve types

Same pattern as abiSizeAdvanced. Fixes compiler crash for nested packed structs.

3 files changed, 63 insertions(+), 57 deletions(-)

src/Sema.zig+3-3
...@@ -11747,11 +11747,11 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -11747,11 +11747,11 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1174711747
11748fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11748fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11749 const inst_data = sema.code.instructions.items(.data)[inst].un_node;11749 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
11750 const src = inst_data.src();
11750 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };11751 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
11751 const unresolved_operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);11752 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
11752 const operand_ty = try sema.resolveTypeFields(block, operand_src, unresolved_operand_ty);
11753 const target = sema.mod.getTarget();11753 const target = sema.mod.getTarget();
11754 const bit_size = operand_ty.bitSize(target);11754 const bit_size = try operand_ty.bitSizeAdvanced(target, sema.kit(block, src));
11755 return sema.addIntUnsigned(Type.comptime_int, bit_size);11755 return sema.addIntUnsigned(Type.comptime_int, bit_size);
11756}11756}
1175711757
src/type.zig+52-51
...@@ -3542,9 +3542,19 @@ pub const Type = extern union {...@@ -3542,9 +3542,19 @@ pub const Type = extern union {
3542 );3542 );
3543 }3543 }
35443544
3545 /// Asserts the type has the bit size already resolved.
3546 pub fn bitSize(ty: Type, target: Target) u64 {3545 pub fn bitSize(ty: Type, target: Target) u64 {
3547 return switch (ty.tag()) {3546 return bitSizeAdvanced(ty, target, null) catch unreachable;
3547 }
3548
3549 /// If you pass `sema_kit`, any recursive type resolutions will happen if
3550 /// necessary, possibly returning a CompileError. Passing `null` instead asserts
3551 /// the type is fully resolved, and there will be no error, guaranteed.
3552 pub fn bitSizeAdvanced(
3553 ty: Type,
3554 target: Target,
3555 sema_kit: ?Module.WipAnalysis,
3556 ) Module.CompileError!u64 {
3557 switch (ty.tag()) {
3548 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer3558 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
3549 .fn_void_no_args => unreachable, // represents machine code; not a pointer3559 .fn_void_no_args => unreachable, // represents machine code; not a pointer
3550 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer3560 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
...@@ -3568,40 +3578,30 @@ pub const Type = extern union {...@@ -3568,40 +3578,30 @@ pub const Type = extern union {
3568 .generic_poison => unreachable,3578 .generic_poison => unreachable,
3569 .bound_fn => unreachable,3579 .bound_fn => unreachable,
35703580
3571 .void => 0,3581 .void => return 0,
3572 .bool, .u1 => 1,3582 .bool, .u1 => return 1,
3573 .u8, .i8 => 8,3583 .u8, .i8 => return 8,
3574 .i16, .u16, .f16 => 16,3584 .i16, .u16, .f16 => return 16,
3575 .u29 => 29,3585 .u29 => return 29,
3576 .i32, .u32, .f32 => 32,3586 .i32, .u32, .f32 => return 32,
3577 .i64, .u64, .f64 => 64,3587 .i64, .u64, .f64 => return 64,
3578 .f80 => 80,3588 .f80 => return 80,
3579 .u128, .i128, .f128 => 128,3589 .u128, .i128, .f128 => return 128,
35803590
3581 .@"struct" => {3591 .@"struct" => {
3582 const field_count = ty.structFieldCount();3592 if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
3583 if (field_count == 0) return 0;3593 var total: u64 = 0;
35843594 for (ty.structFields().values()) |field| {
3585 const struct_obj = ty.castTag(.@"struct").?.data;3595 total += try bitSizeAdvanced(field.ty, target, sema_kit);
3586 assert(struct_obj.haveFieldTypes());
3587
3588 switch (struct_obj.layout) {
3589 .Auto, .Extern => {
3590 var total: u64 = 0;
3591 for (struct_obj.fields.values()) |field| {
3592 total += field.ty.bitSize(target);
3593 }
3594 return total;
3595 },
3596 .Packed => return struct_obj.packedIntegerBits(target),
3597 }3596 }
3597 return total;
3598 },3598 },
35993599
3600 .tuple, .anon_struct => {3600 .tuple, .anon_struct => {
3601 const tuple = ty.tupleFields();3601 if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
3602 var total: u64 = 0;3602 var total: u64 = 0;
3603 for (tuple.types) |field_ty| {3603 for (ty.tupleFields().types) |field_ty| {
3604 total += field_ty.bitSize(target);3604 total += try bitSizeAdvanced(field_ty, target, sema_kit);
3605 }3605 }
3606 return total;3606 return total;
3607 },3607 },
...@@ -3609,37 +3609,35 @@ pub const Type = extern union {...@@ -3609,37 +3609,35 @@ pub const Type = extern union {
3609 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {3609 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
3610 var buffer: Payload.Bits = undefined;3610 var buffer: Payload.Bits = undefined;
3611 const int_tag_ty = ty.intTagType(&buffer);3611 const int_tag_ty = ty.intTagType(&buffer);
3612 return int_tag_ty.bitSize(target);3612 return try bitSizeAdvanced(int_tag_ty, target, sema_kit);
3613 },3613 },
36143614
3615 .@"union", .union_tagged => {3615 .@"union", .union_tagged => {
3616 if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
3616 const union_obj = ty.cast(Payload.Union).?.data;3617 const union_obj = ty.cast(Payload.Union).?.data;
3617
3618 const fields = union_obj.fields;
3619 if (fields.count() == 0) return 0;
3620
3621 assert(union_obj.haveFieldTypes());3618 assert(union_obj.haveFieldTypes());
36223619
3623 var size: u64 = 0;3620 var size: u64 = 0;
3624 for (fields.values()) |field| {3621 for (union_obj.fields.values()) |field| {
3625 size = @maximum(size, field.ty.bitSize(target));3622 size = @maximum(size, try bitSizeAdvanced(field.ty, target, sema_kit));
3626 }3623 }
3627 return size;3624 return size;
3628 },3625 },
36293626
3630 .vector => {3627 .vector => {
3631 const payload = ty.castTag(.vector).?.data;3628 const payload = ty.castTag(.vector).?.data;
3632 const elem_bit_size = payload.elem_type.bitSize(target);3629 const elem_bit_size = try bitSizeAdvanced(payload.elem_type, target, sema_kit);
3633 return elem_bit_size * payload.len;3630 return elem_bit_size * payload.len;
3634 },3631 },
3635 .array_u8 => 8 * ty.castTag(.array_u8).?.data,3632 .array_u8 => return 8 * ty.castTag(.array_u8).?.data,
3636 .array_u8_sentinel_0 => 8 * (ty.castTag(.array_u8_sentinel_0).?.data + 1),3633 .array_u8_sentinel_0 => return 8 * (ty.castTag(.array_u8_sentinel_0).?.data + 1),
3637 .array => {3634 .array => {
3638 const payload = ty.castTag(.array).?.data;3635 const payload = ty.castTag(.array).?.data;
3639 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));3636 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
3640 if (elem_size == 0 or payload.len == 0)3637 if (elem_size == 0 or payload.len == 0)
3641 return 0;3638 return @as(u64, 0);
3642 return (payload.len - 1) * 8 * elem_size + payload.elem_type.bitSize(target);3639 const elem_bit_size = try bitSizeAdvanced(payload.elem_type, target, sema_kit);
3640 return (payload.len - 1) * 8 * elem_size + elem_bit_size;
3643 },3641 },
3644 .array_sentinel => {3642 .array_sentinel => {
3645 const payload = ty.castTag(.array_sentinel).?.data;3643 const payload = ty.castTag(.array_sentinel).?.data;
...@@ -3647,14 +3645,15 @@ pub const Type = extern union {...@@ -3647,14 +3645,15 @@ pub const Type = extern union {
3647 payload.elem_type.abiAlignment(target),3645 payload.elem_type.abiAlignment(target),
3648 payload.elem_type.abiSize(target),3646 payload.elem_type.abiSize(target),
3649 );3647 );
3650 return payload.len * 8 * elem_size + payload.elem_type.bitSize(target);3648 const elem_bit_size = try bitSizeAdvanced(payload.elem_type, target, sema_kit);
3649 return payload.len * 8 * elem_size + elem_bit_size;
3651 },3650 },
36523651
3653 .isize,3652 .isize,
3654 .usize,3653 .usize,
3655 .@"anyframe",3654 .@"anyframe",
3656 .anyframe_T,3655 .anyframe_T,
3657 => target.cpu.arch.ptrBitWidth(),3656 => return target.cpu.arch.ptrBitWidth(),
36583657
3659 .const_slice,3658 .const_slice,
3660 .mut_slice,3659 .mut_slice,
...@@ -3662,7 +3661,7 @@ pub const Type = extern union {...@@ -3662,7 +3661,7 @@ pub const Type = extern union {
36623661
3663 .const_slice_u8,3662 .const_slice_u8,
3664 .const_slice_u8_sentinel_0,3663 .const_slice_u8_sentinel_0,
3665 => target.cpu.arch.ptrBitWidth() * 2,3664 => return target.cpu.arch.ptrBitWidth() * 2,
36663665
3667 .optional_single_const_pointer,3666 .optional_single_const_pointer,
3668 .optional_single_mut_pointer,3667 .optional_single_mut_pointer,
...@@ -3681,8 +3680,8 @@ pub const Type = extern union {...@@ -3681,8 +3680,8 @@ pub const Type = extern union {
3681 },3680 },
36823681
3683 .pointer => switch (ty.castTag(.pointer).?.data.size) {3682 .pointer => switch (ty.castTag(.pointer).?.data.size) {
3684 .Slice => target.cpu.arch.ptrBitWidth() * 2,3683 .Slice => return target.cpu.arch.ptrBitWidth() * 2,
3685 else => target.cpu.arch.ptrBitWidth(),3684 else => return target.cpu.arch.ptrBitWidth(),
3686 },3685 },
36873686
3688 .manyptr_u8,3687 .manyptr_u8,
...@@ -3708,7 +3707,7 @@ pub const Type = extern union {...@@ -3708,7 +3707,7 @@ pub const Type = extern union {
3708 .error_set_merged,3707 .error_set_merged,
3709 => return 16, // TODO revisit this when we have the concept of the error tag type3708 => return 16, // TODO revisit this when we have the concept of the error tag type
37103709
3711 .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data,3710 .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data,
37123711
3713 .optional => {3712 .optional => {
3714 var buf: Payload.ElemType = undefined;3713 var buf: Payload.ElemType = undefined;
...@@ -3722,7 +3721,8 @@ pub const Type = extern union {...@@ -3722,7 +3721,8 @@ pub const Type = extern union {
3722 // field and a boolean as the second. Since the child type's abi alignment is3721 // field and a boolean as the second. Since the child type's abi alignment is
3723 // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal3722 // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
3724 // to the child type's ABI alignment.3723 // to the child type's ABI alignment.
3725 return child_type.bitSize(target) + 1;3724 const child_bit_size = try bitSizeAdvanced(child_type, target, sema_kit);
3725 return child_bit_size + 1;
3726 },3726 },
37273727
3728 .error_union => {3728 .error_union => {
...@@ -3730,9 +3730,9 @@ pub const Type = extern union {...@@ -3730,9 +3730,9 @@ pub const Type = extern union {
3730 if (!payload.error_set.hasRuntimeBits() and !payload.payload.hasRuntimeBits()) {3730 if (!payload.error_set.hasRuntimeBits() and !payload.payload.hasRuntimeBits()) {
3731 return 0;3731 return 0;
3732 } else if (!payload.error_set.hasRuntimeBits()) {3732 } else if (!payload.error_set.hasRuntimeBits()) {
3733 return payload.payload.bitSize(target);3733 return payload.payload.bitSizeAdvanced(target, sema_kit);
3734 } else if (!payload.payload.hasRuntimeBits()) {3734 } else if (!payload.payload.hasRuntimeBits()) {
3735 return payload.error_set.bitSize(target);3735 return payload.error_set.bitSizeAdvanced(target, sema_kit);
3736 }3736 }
3737 @panic("TODO bitSize error union");3737 @panic("TODO bitSize error union");
3738 },3738 },
...@@ -3749,7 +3749,7 @@ pub const Type = extern union {...@@ -3749,7 +3749,7 @@ pub const Type = extern union {
3749 .extern_options,3749 .extern_options,
3750 .type_info,3750 .type_info,
3751 => @panic("TODO at some point we gotta resolve builtin types"),3751 => @panic("TODO at some point we gotta resolve builtin types"),
3752 };3752 }
3753 }3753 }
37543754
3755 pub fn isSinglePointer(self: Type) bool {3755 pub fn isSinglePointer(self: Type) bool {
...@@ -5506,6 +5506,7 @@ pub const Type = extern union {...@@ -5506,6 +5506,7 @@ pub const Type = extern union {
5506 switch (ty.tag()) {5506 switch (ty.tag()) {
5507 .@"struct" => {5507 .@"struct" => {
5508 const struct_obj = ty.castTag(.@"struct").?.data;5508 const struct_obj = ty.castTag(.@"struct").?.data;
5509 assert(struct_obj.haveFieldTypes());
5509 return struct_obj.fields.count();5510 return struct_obj.fields.count();
5510 },5511 },
5511 .empty_struct, .empty_struct_literal => return 0,5512 .empty_struct, .empty_struct_literal => return 0,
test/behavior/packed-struct.zig+8-3
...@@ -243,7 +243,12 @@ test "correct sizeOf and offsets in packed structs" {...@@ -243,7 +243,12 @@ test "correct sizeOf and offsets in packed structs" {
243}243}
244244
245test "nested packed structs" {245test "nested packed structs" {
246 if (builtin.zig_backend != .stage1) return error.SkipZigTest;246 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
247 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
248 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
249 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
250 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
251 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
247252
248 const S1 = packed struct { a: u8, b: u8, c: u8 };253 const S1 = packed struct { a: u8, b: u8, c: u8 };
249254
...@@ -253,7 +258,7 @@ test "nested packed structs" {...@@ -253,7 +258,7 @@ test "nested packed structs" {
253 const S3Padded = packed struct { s3: S3, pad: u16 };258 const S3Padded = packed struct { s3: S3, pad: u16 };
254259
255 try expectEqual(48, @bitSizeOf(S3));260 try expectEqual(48, @bitSizeOf(S3));
256 try expectEqual(6, @sizeOf(S3));261 try expectEqual(@sizeOf(u48), @sizeOf(S3));
257262
258 try expectEqual(3, @offsetOf(S3, "y"));263 try expectEqual(3, @offsetOf(S3, "y"));
259 try expectEqual(24, @bitOffsetOf(S3, "y"));264 try expectEqual(24, @bitOffsetOf(S3, "y"));
...@@ -273,7 +278,7 @@ test "nested packed structs" {...@@ -273,7 +278,7 @@ test "nested packed structs" {
273 const S6 = packed struct { a: i32, b: S4, c: i8 };278 const S6 = packed struct { a: i32, b: S4, c: i8 };
274279
275 const expectedBitSize = 80;280 const expectedBitSize = 80;
276 const expectedByteSize = expectedBitSize / 8;281 const expectedByteSize = @sizeOf(u80);
277 try expectEqual(expectedBitSize, @bitSizeOf(S5));282 try expectEqual(expectedBitSize, @bitSizeOf(S5));
278 try expectEqual(expectedByteSize, @sizeOf(S5));283 try expectEqual(expectedByteSize, @sizeOf(S5));
279 try expectEqual(expectedBitSize, @bitSizeOf(S6));284 try expectEqual(expectedBitSize, @bitSizeOf(S6));