diff options
Diffstat (limited to 'src/type.zig')
| -rw-r--r-- | src/type.zig | 291 |
1 files changed, 154 insertions, 137 deletions
diff --git a/src/type.zig b/src/type.zig index cc00f712f0..ea65cc8916 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2760,7 +2760,7 @@ pub const Type = extern union { .sema_kit => |sk| sk, else => null, }; - return switch (ty.tag()) { + switch (ty.tag()) { .u1, .u8, .i8, @@ -3028,7 +3028,7 @@ pub const Type = extern union { => unreachable, .generic_poison => unreachable, - }; + } } pub fn abiAlignmentAdvancedUnion( @@ -3076,10 +3076,37 @@ pub const Type = extern union { return AbiAlignmentAdvanced{ .scalar = max_align }; } + /// May capture a reference to `ty`. + pub fn lazyAbiSize(ty: Type, target: Target, arena: Allocator) !Value { + switch (try ty.abiSizeAdvanced(target, .{ .lazy = arena })) { + .val => |val| return val, + .scalar => |x| return Value.Tag.int_u64.create(arena, x), + } + } + /// Asserts the type has the ABI size already resolved. /// Types that return false for hasRuntimeBits() return 0. - pub fn abiSize(self: Type, target: Target) u64 { - return switch (self.tag()) { + pub fn abiSize(ty: Type, target: Target) u64 { + return (abiSizeAdvanced(ty, target, .eager) catch unreachable).scalar; + } + + const AbiSizeAdvanced = union(enum) { + scalar: u64, + val: Value, + }; + + /// If you pass `eager` you will get back `scalar` and assert the type is resolved. + /// In this case there will be no error, guaranteed. + /// If you pass `lazy` you may get back `scalar` or `val`. + /// If `val` is returned, a reference to `ty` has been captured. + /// If you pass `sema_kit` you will get back `scalar` and resolve the type if + /// necessary, possibly returning a CompileError. + pub fn abiSizeAdvanced( + ty: Type, + target: Target, + strat: AbiAlignmentAdvancedStrat, + ) Module.CompileError!AbiSizeAdvanced { + switch (ty.tag()) { .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer .fn_void_no_args => unreachable, // represents machine code; not a pointer .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer @@ -3109,32 +3136,59 @@ pub const Type = extern union { .empty_struct_literal, .empty_struct, .void, - => 0, + => return AbiSizeAdvanced{ .scalar = 0 }, - .@"struct", .tuple, .anon_struct => switch (self.containerLayout()) { + .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) { .Packed => { - const struct_obj = self.castTag(.@"struct").?.data; + const struct_obj = ty.castTag(.@"struct").?.data; + switch (strat) { + .sema_kit => |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty), + .lazy => |arena| { + if (!struct_obj.haveFieldTypes()) { + return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; + } + }, + .eager => {}, + } var buf: Type.Payload.Bits = undefined; const int_ty = struct_obj.packedIntegerType(target, &buf); - return int_ty.abiSize(target); + return AbiSizeAdvanced{ .scalar = int_ty.abiSize(target) }; }, else => { - const field_count = self.structFieldCount(); + switch (strat) { + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), + .lazy => |arena| { + if (ty.castTag(.@"struct")) |payload| { + const struct_obj = payload.data; + if (!struct_obj.haveLayout()) { + return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; + } + } + }, + .eager => {}, + } + const field_count = ty.structFieldCount(); if (field_count == 0) { - return 0; + return AbiSizeAdvanced{ .scalar = 0 }; } - return self.structFieldOffset(field_count, target); + return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, target) }; }, }, .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { var buffer: Payload.Bits = undefined; - const int_tag_ty = self.intTagType(&buffer); - return int_tag_ty.abiSize(target); + const int_tag_ty = ty.intTagType(&buffer); + return AbiSizeAdvanced{ .scalar = int_tag_ty.abiSize(target) }; + }, + .@"union" => { + const union_obj = ty.castTag(.@"union").?.data; + // TODO pass `true` for have_tag when unions have a safety tag + return abiSizeAdvancedUnion(ty, target, strat, union_obj, false); + }, + .union_tagged => { + const union_obj = ty.castTag(.union_tagged).?.data; + return abiSizeAdvancedUnion(ty, target, strat, union_obj, true); }, - // TODO pass `true` for have_tag when unions have a safety tag - .@"union" => return self.castTag(.@"union").?.data.abiSize(target, false), - .union_tagged => return self.castTag(.union_tagged).?.data.abiSize(target, true), .u1, .u8, @@ -3146,21 +3200,31 @@ pub const Type = extern union { .address_space, .float_mode, .reduce_op, - => return 1, + => return AbiSizeAdvanced{ .scalar = 1 }, - .array_u8 => self.castTag(.array_u8).?.data, - .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1, + .array_u8 => return AbiSizeAdvanced{ .scalar = ty.castTag(.array_u8).?.data }, + .array_u8_sentinel_0 => return AbiSizeAdvanced{ .scalar = ty.castTag(.array_u8_sentinel_0).?.data + 1 }, .array, .vector => { - const payload = self.cast(Payload.Array).?.data; - const elem_size = payload.elem_type.abiSize(target); - assert(elem_size >= payload.elem_type.abiAlignment(target)); - return payload.len * elem_size; + const payload = ty.cast(Payload.Array).?.data; + switch (try payload.elem_type.abiSizeAdvanced(target, strat)) { + .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = payload.len * elem_size }, + .val => switch (strat) { + .sema_kit => unreachable, + .eager => unreachable, + .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }, + }, + } }, .array_sentinel => { - const payload = self.castTag(.array_sentinel).?.data; - const elem_size = payload.elem_type.abiSize(target); - assert(elem_size >= payload.elem_type.abiAlignment(target)); - return (payload.len + 1) * elem_size; + const payload = ty.castTag(.array_sentinel).?.data; + switch (try payload.elem_type.abiSizeAdvanced(target, strat)) { + .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = (payload.len + 1) * elem_size }, + .val => switch (strat) { + .sema_kit => unreachable, + .eager => unreachable, + .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }, + }, + } }, .isize, @@ -3178,95 +3242,96 @@ pub const Type = extern union { .manyptr_u8, .manyptr_const_u8, .manyptr_const_u8_sentinel_0, - => return @divExact(target.cpu.arch.ptrBitWidth(), 8), + => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, .const_slice, .mut_slice, .const_slice_u8, .const_slice_u8_sentinel_0, - => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, + => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2 }, - .pointer => switch (self.castTag(.pointer).?.data.size) { - .Slice => @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, - else => @divExact(target.cpu.arch.ptrBitWidth(), 8), + .pointer => switch (ty.castTag(.pointer).?.data.size) { + .Slice => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2 }, + else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, }, - .c_short => return @divExact(CType.short.sizeInBits(target), 8), - .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), - .c_int => return @divExact(CType.int.sizeInBits(target), 8), - .c_uint => return @divExact(CType.uint.sizeInBits(target), 8), - .c_long => return @divExact(CType.long.sizeInBits(target), 8), - .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8), - .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8), - .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8), + .c_short => return AbiSizeAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, + .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, + .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, + .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, + .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, + .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, + .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, + .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, - .f16 => return 2, - .f32 => return 4, - .f64 => return 8, - .f128 => return 16, + .f16 => return AbiSizeAdvanced{ .scalar = 2 }, + .f32 => return AbiSizeAdvanced{ .scalar = 4 }, + .f64 => return AbiSizeAdvanced{ .scalar = 8 }, + .f128 => return AbiSizeAdvanced{ .scalar = 16 }, .f80 => switch (target.cpu.arch) { - .i386 => return 12, - .x86_64 => return 16, + .i386 => return AbiSizeAdvanced{ .scalar = 12 }, + .x86_64 => return AbiSizeAdvanced{ .scalar = 16 }, else => { var payload: Payload.Bits = .{ .base = .{ .tag = .int_unsigned }, .data = 80, }; const u80_ty = initPayload(&payload.base); - return abiSize(u80_ty, target); + return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) }; }, }, .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { - 16 => return abiSize(Type.f16, target), - 32 => return abiSize(Type.f32, target), - 64 => return abiSize(Type.f64, target), - 80 => return abiSize(Type.f80, target), - 128 => return abiSize(Type.f128, target), + 16 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f16, target) }, + 32 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f32, target) }, + 64 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f64, target) }, + 80 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f80, target) }, + 128 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f128, target) }, else => unreachable, }, + // TODO revisit this when we have the concept of the error tag type .error_set, .error_set_single, .anyerror_void_error_union, .anyerror, .error_set_inferred, .error_set_merged, - => return 2, // TODO revisit this when we have the concept of the error tag type + => return AbiSizeAdvanced{ .scalar = 2 }, - .i16, .u16 => return intAbiSize(16, target), - .i32, .u32 => return intAbiSize(32, target), - .i64, .u64 => return intAbiSize(64, target), - .u128, .i128 => return intAbiSize(128, target), + .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) }, + .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) }, + .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) }, + .u128, .i128 => return AbiSizeAdvanced{ .scalar = intAbiSize(128, target) }, .int_signed, .int_unsigned => { - const bits: u16 = self.cast(Payload.Bits).?.data; - if (bits == 0) return 0; - return intAbiSize(bits, target); + const bits: u16 = ty.cast(Payload.Bits).?.data; + if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; + return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target) }; }, .optional => { var buf: Payload.ElemType = undefined; - const child_type = self.optionalChild(&buf); - if (!child_type.hasRuntimeBits()) return 1; + const child_type = ty.optionalChild(&buf); + if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) - return @divExact(target.cpu.arch.ptrBitWidth(), 8); + return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; // Optional types are represented as a struct with the child type as the first // field and a boolean as the second. Since the child type's abi alignment is // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal // to the child type's ABI alignment. - return child_type.abiAlignment(target) + child_type.abiSize(target); + return AbiSizeAdvanced{ .scalar = child_type.abiAlignment(target) + child_type.abiSize(target) }; }, .error_union => { - const data = self.castTag(.error_union).?.data; + const data = ty.castTag(.error_union).?.data; if (!data.error_set.hasRuntimeBits() and !data.payload.hasRuntimeBits()) { - return 0; + return AbiSizeAdvanced{ .scalar = 0 }; } else if (!data.error_set.hasRuntimeBits()) { - return data.payload.abiSize(target); + return AbiSizeAdvanced{ .scalar = data.payload.abiSize(target) }; } else if (!data.payload.hasRuntimeBits()) { - return data.error_set.abiSize(target); + return AbiSizeAdvanced{ .scalar = data.error_set.abiSize(target) }; } const code_align = abiAlignment(data.error_set, target); const payload_align = abiAlignment(data.payload, target); @@ -3278,9 +3343,28 @@ pub const Type = extern union { size = std.mem.alignForwardGeneric(u64, size, payload_align); size += payload_size; size = std.mem.alignForwardGeneric(u64, size, big_align); - return size; + return AbiSizeAdvanced{ .scalar = size }; }, - }; + } + } + + pub fn abiSizeAdvancedUnion( + ty: Type, + target: Target, + strat: AbiAlignmentAdvancedStrat, + union_obj: *Module.Union, + have_tag: bool, + ) Module.CompileError!AbiSizeAdvanced { + switch (strat) { + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), + .lazy => |arena| { + if (!union_obj.haveLayout()) { + return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; + } + }, + .eager => {}, + } + return AbiSizeAdvanced{ .scalar = union_obj.abiSize(target, have_tag) }; } fn intAbiSize(bits: u16, target: Target) u64 { @@ -5448,73 +5532,6 @@ pub const Type = extern union { } } - /// Asserts the type is an enum. - pub fn enumHasInt(ty: Type, int: Value, mod: *Module) bool { - const S = struct { - fn intInRange(tag_ty: Type, int_val: Value, end: usize, m: *Module) bool { - if (int_val.compareWithZero(.lt)) return false; - var end_payload: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = end, - }; - const end_val = Value.initPayload(&end_payload.base); - if (int_val.compare(.gte, end_val, tag_ty, m)) return false; - return true; - } - }; - switch (ty.tag()) { - .enum_nonexhaustive => return int.intFitsInType(ty, mod.getTarget()), - .enum_full => { - const enum_full = ty.castTag(.enum_full).?.data; - const tag_ty = enum_full.tag_ty; - if (enum_full.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_full.fields.count(), mod); - } else { - return enum_full.values.containsContext(int, .{ - .ty = tag_ty, - .mod = mod, - }); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - const tag_ty = enum_obj.tag_ty; - if (enum_obj.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_obj.fields.count(), mod); - } else { - return enum_obj.values.containsContext(int, .{ - .ty = tag_ty, - .mod = mod, - }); - } - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const fields_len = enum_simple.fields.count(); - const bits = std.math.log2_int_ceil(usize, fields_len); - var buffer: Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = bits, - }; - const tag_ty = Type.initPayload(&buffer.base); - return S.intInRange(tag_ty, int, fields_len, mod); - }, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .call_options, - .prefetch_options, - .export_options, - .extern_options, - => unreachable, - - else => unreachable, - } - } - /// This enum does not directly correspond to `std.builtin.TypeId` because /// it has extra enum tags in it, as a way of using less memory. For example, /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
