aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCody Tapscott <topolarity@tapscott.me>2022-03-11 14:18:23 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-03-14 21:42:42 -0700
commit34a6fcd88e20a2491bf6a5396d583a7449cac20b (patch)
tree02eba522b43876b9145d8369332557d47d3f25c5 /src
parentbbd750ff05895f29be646bf51e8932c3c9fb14f3 (diff)
downloadzig-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.zig10
-rw-r--r--src/Sema.zig208
-rw-r--r--src/type.zig144
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()) {