diff options
| author | Cody Tapscott <topolarity@tapscott.me> | 2022-03-11 14:18:23 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-03-14 21:42:42 -0700 |
| commit | 34a6fcd88e20a2491bf6a5396d583a7449cac20b (patch) | |
| tree | 02eba522b43876b9145d8369332557d47d3f25c5 /src | |
| parent | bbd750ff05895f29be646bf51e8932c3c9fb14f3 (diff) | |
| download | zig-34a6fcd88e20a2491bf6a5396d583a7449cac20b.tar.gz zig-34a6fcd88e20a2491bf6a5396d583a7449cac20b.zip | |
stage2: Add hasWellDefinedLayout() to type.zig and Sema.zig
This follows the same strategy as sema.typeRequiresComptime() and
type.comptimeOnly(): Two versions of the function, one which performs
resolution just-in-time and another which asserts that resolution is
complete.
Thankfully, this doesn't cause very viral type resolution, since
auto-layout structs and unions are very common and are known to not have
a well-defined layout without resolving their fields.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 10 | ||||
| -rw-r--r-- | src/Sema.zig | 208 | ||||
| -rw-r--r-- | src/type.zig | 144 |
3 files changed, 348 insertions, 14 deletions
diff --git a/src/Module.zig b/src/Module.zig index b6104df232..7c6c654660 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -852,7 +852,7 @@ pub const ErrorSet = struct { } }; -pub const RequiresComptime = enum { no, yes, unknown, wip }; +pub const PropertyBoolean = enum { no, yes, unknown, wip }; /// Represents the data that a struct declaration provides. pub const Struct = struct { @@ -884,7 +884,8 @@ pub const Struct = struct { /// If false, resolving the fields is necessary to determine whether the type has only /// one possible value. known_non_opv: bool, - requires_comptime: RequiresComptime = .unknown, + requires_comptime: PropertyBoolean = .unknown, + has_well_defined_layout: PropertyBoolean = .unknown, pub const Fields = std.StringArrayHashMapUnmanaged(Field); @@ -1079,6 +1080,8 @@ pub const EnumFull = struct { /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, + /// true if zig inferred this tag type, false if user specified it + tag_ty_inferred: bool, /// Set of field names in declaration order. fields: NameMap, /// Maps integer tag value to field index. @@ -1132,7 +1135,8 @@ pub const Union = struct { // which `have_layout` does not ensure. fully_resolved, }, - requires_comptime: RequiresComptime = .unknown, + requires_comptime: PropertyBoolean = .unknown, + has_well_defined_layout: PropertyBoolean = .unknown, pub const Field = struct { /// undefined until `status` is `have_field_types` or `have_layout`. diff --git a/src/Sema.zig b/src/Sema.zig index 5fa7866586..bf1b24145d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1579,6 +1579,8 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const target = sema.mod.getTarget(); const addr_space = target_util.defaultAddressSpace(target, .local); + try sema.resolveTypeLayout(block, src, pointee_ty); + if (Air.refToIndex(ptr)) |ptr_inst| { if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) { const air_datas = sema.air_instructions.items(.data); @@ -1885,6 +1887,7 @@ fn zirEnumDecl( enum_obj.* = .{ .owner_decl = new_decl, .tag_ty = Type.initTag(.@"null"), + .tag_ty_inferred = true, .fields = .{}, .values = .{}, .node_offset = src.node_offset, @@ -1907,6 +1910,7 @@ fn zirEnumDecl( // TODO better source location const ty = try sema.resolveType(block, src, tag_type_ref); enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); + enum_obj.tag_ty_inferred = false; } try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl); @@ -1956,16 +1960,16 @@ fn zirEnumDecl( try wip_captures.finalize(); - const tag_ty = blk: { - if (tag_type_ref != .none) { - // TODO better source location - const ty = try sema.resolveType(block, src, tag_type_ref); - break :blk try ty.copy(new_decl_arena_allocator); - } + if (tag_type_ref != .none) { + // TODO better source location + const ty = try sema.resolveType(block, src, tag_type_ref); + enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); + enum_obj.tag_ty_inferred = false; + } else { const bits = std.math.log2_int_ceil(usize, fields_len); - break :blk try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits); - }; - enum_obj.tag_ty = tag_ty; + enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits); + enum_obj.tag_ty_inferred = true; + } } try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); @@ -2417,13 +2421,13 @@ fn zirAllocExtended( try sema.validateVarType(block, ty_src, var_ty, false); } const target = sema.mod.getTarget(); + try sema.requireRuntimeBlock(block, src); + try sema.resolveTypeLayout(block, src, var_ty); const ptr_type = try Type.ptr(sema.arena, target, .{ .pointee_type = var_ty, .@"align" = alignment, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); - try sema.requireRuntimeBlock(block, src); - try sema.resolveTypeLayout(block, src, var_ty); return block.addTy(.alloc, ptr_type); } @@ -21209,6 +21213,182 @@ fn typePtrOrOptionalPtrTy( } } +fn typeHasWellDefinedLayout(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { + return switch (ty.tag()) { + .u1, + .u8, + .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, + .u128, + .i128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .f16, + .f32, + .f64, + .f80, + .f128, + .bool, + .void, + .manyptr_u8, + .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, + .anyerror_void_error_union, + .empty_struct_literal, + .empty_struct, + .array_u8, + .array_u8_sentinel_0, + .int_signed, + .int_unsigned, + .pointer, + .single_const_pointer, + .single_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + .single_const_pointer_to_comptime_int, + .enum_numbered, + => true, + + .anyopaque, + .anyerror, + .noreturn, + .@"null", + .@"anyframe", + .@"undefined", + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_options, + .prefetch_options, + .export_options, + .extern_options, + .error_set, + .error_set_single, + .error_set_inferred, + .error_set_merged, + .@"opaque", + .generic_poison, + .type, + .comptime_int, + .comptime_float, + .enum_literal, + .type_info, + // These are function bodies, not function pointers. + .fn_noreturn_no_args, + .fn_void_no_args, + .fn_naked_noreturn_no_args, + .fn_ccc_void_no_args, + .function, + .const_slice_u8, + .const_slice_u8_sentinel_0, + .const_slice, + .mut_slice, + .enum_simple, + .error_union, + .anyframe_T, + .tuple, + .anon_struct, + => false, + + .enum_full, + .enum_nonexhaustive, + => !ty.cast(Type.Payload.EnumFull).?.data.tag_ty_inferred, + + .var_args_param => unreachable, + .inferred_alloc_mut => unreachable, + .inferred_alloc_const => unreachable, + .bound_fn => unreachable, + + .array, + .array_sentinel, + .vector, + => sema.typeHasWellDefinedLayout(block, src, ty.childType()), + + .optional, + .optional_single_mut_pointer, + .optional_single_const_pointer, + => blk: { + var buf: Type.Payload.ElemType = undefined; + break :blk sema.typeHasWellDefinedLayout(block, src, ty.optionalChild(&buf)); + }, + + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + if (struct_obj.layout == .Auto) { + struct_obj.has_well_defined_layout = .no; + return false; + } + switch (struct_obj.has_well_defined_layout) { + .no => return false, + .yes, .wip => return true, + .unknown => { + if (struct_obj.status == .field_types_wip) + return true; + + try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj); + + struct_obj.has_well_defined_layout = .wip; + for (struct_obj.fields.values()) |field| { + if (!(try sema.typeHasWellDefinedLayout(block, src, field.ty))) { + struct_obj.has_well_defined_layout = .no; + return false; + } + } + struct_obj.has_well_defined_layout = .yes; + return true; + }, + } + }, + + .@"union", .union_tagged => { + const union_obj = ty.cast(Type.Payload.Union).?.data; + if (union_obj.layout == .Auto) { + union_obj.has_well_defined_layout = .no; + return false; + } + switch (union_obj.has_well_defined_layout) { + .no => return false, + .yes, .wip => return true, + .unknown => { + if (union_obj.status == .field_types_wip) + return true; + + try sema.resolveTypeFieldsUnion(block, src, ty, union_obj); + + union_obj.has_well_defined_layout = .wip; + for (union_obj.fields.values()) |field| { + if (!(try sema.typeHasWellDefinedLayout(block, src, field.ty))) { + union_obj.has_well_defined_layout = .no; + return false; + } + } + union_obj.has_well_defined_layout = .yes; + return true; + }, + } + }, + }; +} + /// `generic_poison` will return false. /// This function returns false negatives when structs and unions are having their /// field types resolved. @@ -21412,6 +21592,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) return true; } +fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 { + try sema.resolveTypeLayout(block, src, ty); + const target = sema.mod.getTarget(); + return ty.abiSize(target); +} + fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 { try sema.resolveTypeLayout(block, src, ty); const target = sema.mod.getTarget(); diff --git a/src/type.zig b/src/type.zig index 89f0e84b70..9ea579ee19 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2173,6 +2173,149 @@ pub const Type = extern union { }; } + /// true if and only if the type has a well-defined memory layout + /// readFrom/writeToMemory are supported only for types with a well- + /// defined memory layout + pub fn hasWellDefinedLayout(ty: Type) bool { + return switch (ty.tag()) { + .u1, + .u8, + .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, + .u128, + .i128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .f16, + .f32, + .f64, + .f80, + .f128, + .bool, + .void, + .manyptr_u8, + .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, + .anyerror_void_error_union, + .empty_struct_literal, + .empty_struct, + .array_u8, + .array_u8_sentinel_0, + .int_signed, + .int_unsigned, + .pointer, + .single_const_pointer, + .single_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + .single_const_pointer_to_comptime_int, + .enum_numbered, + => true, + + .anyopaque, + .anyerror, + .noreturn, + .@"null", + .@"anyframe", + .@"undefined", + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_options, + .prefetch_options, + .export_options, + .extern_options, + .error_set, + .error_set_single, + .error_set_inferred, + .error_set_merged, + .@"opaque", + .generic_poison, + .type, + .comptime_int, + .comptime_float, + .enum_literal, + .type_info, + // These are function bodies, not function pointers. + .fn_noreturn_no_args, + .fn_void_no_args, + .fn_naked_noreturn_no_args, + .fn_ccc_void_no_args, + .function, + .const_slice_u8, + .const_slice_u8_sentinel_0, + .const_slice, + .mut_slice, + .enum_simple, + .error_union, + .anyframe_T, + .tuple, + .anon_struct, + => false, + + .enum_full, + .enum_nonexhaustive, + => !ty.cast(Payload.EnumFull).?.data.tag_ty_inferred, + + .var_args_param => unreachable, + .inferred_alloc_mut => unreachable, + .inferred_alloc_const => unreachable, + .bound_fn => unreachable, + + .array, + .array_sentinel, + .vector, + => ty.childType().hasWellDefinedLayout(), + + .optional, + .optional_single_mut_pointer, + .optional_single_const_pointer, + => { + var buf: Type.Payload.ElemType = undefined; + return ty.optionalChild(&buf).hasWellDefinedLayout(); + }, + + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + if (struct_obj.layout == .Auto) return false; + switch (struct_obj.has_well_defined_layout) { + .wip, .unknown => unreachable, // This function asserts types already resolved. + .no => return false, + .yes => return true, + } + }, + + .@"union", .union_tagged => { + const union_obj = ty.cast(Type.Payload.Union).?.data; + if (union_obj.layout == .Auto) return false; + switch (union_obj.has_well_defined_layout) { + .wip, .unknown => unreachable, // This function asserts types already resolved. + .no => return false, + .yes => return true, + } + }, + }; + } + pub fn hasRuntimeBits(ty: Type) bool { return hasRuntimeBitsAdvanced(ty, false); } @@ -3263,6 +3406,7 @@ pub const Type = extern union { /// For ?[*]T, returns T. /// For *T, returns T. /// For [*]T, returns T. + /// For [N]T, returns T. /// For []T, returns T. pub fn elemType2(ty: Type) Type { return switch (ty.tag()) { |
