diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-03-14 21:11:49 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-03-14 21:43:03 -0700 |
| commit | 2f92d1a0264b6827cb67a55726c4c9a082337508 (patch) | |
| tree | d6691a25a828b44cb4c17365fb8549d339046810 /src | |
| parent | 50a1ca24ca2a4311097132d660b8244f252da82f (diff) | |
| download | zig-2f92d1a0264b6827cb67a55726c4c9a082337508.tar.gz zig-2f92d1a0264b6827cb67a55726c4c9a082337508.zip | |
stage2: fixups for topolarity-comptime-memory-reinterp branch
* don't store `has_well_defined_layout` in memory.
* remove struct `hasWellDefinedLayout` logic. it's just
`layout != .Auto`. This means we only need one implementation, in
Type.
* fix some of the cases being wrong in `hasWellDefinedLayout`, such as
optional pointers.
* move `tag_ty_inferred` field into a position that makes it more
obvious how the struct layout will be done. Also we don't have a
compiler that intelligently moves fields around so this layout is
better.
* Sema: don't `resolveTypeLayout` in `zirCoerceResultPtr` unless
necessary.
* Rename `ComptimePtrLoadKit` `target` field to `pointee` to avoid
confusion with `target`.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 6 | ||||
| -rw-r--r-- | src/Sema.zig | 235 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 5 | ||||
| -rw-r--r-- | src/type.zig | 51 | ||||
| -rw-r--r-- | src/value.zig | 4 |
5 files changed, 53 insertions, 248 deletions
diff --git a/src/Module.zig b/src/Module.zig index 7c6c654660..20bf25af03 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -885,7 +885,6 @@ pub const Struct = struct { /// one possible value. known_non_opv: bool, requires_comptime: PropertyBoolean = .unknown, - has_well_defined_layout: PropertyBoolean = .unknown, pub const Fields = std.StringArrayHashMapUnmanaged(Field); @@ -1080,8 +1079,6 @@ 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. @@ -1092,6 +1089,8 @@ pub const EnumFull = struct { namespace: Namespace, /// Offset from `owner_decl`, points to the enum decl AST node. node_offset: i32, + /// true if zig inferred this tag type, false if user specified it + tag_ty_inferred: bool, pub const NameMap = std.StringArrayHashMapUnmanaged(void); pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false); @@ -1136,7 +1135,6 @@ pub const Union = struct { fully_resolved, }, 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 ad96aea7ab..57522bcfd5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1579,8 +1579,6 @@ 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); @@ -1617,6 +1615,9 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE try pointee_ty.copy(anon_decl.arena()), Value.undef, ); + if (iac.data.alignment != 0) { + try sema.resolveTypeLayout(block, src, pointee_ty); + } const ptr_ty = try Type.ptr(sema.arena, target, .{ .pointee_type = pointee_ty, .@"align" = iac.data.alignment, @@ -1886,7 +1887,7 @@ fn zirEnumDecl( enum_obj.* = .{ .owner_decl = new_decl, - .tag_ty = Type.initTag(.@"null"), + .tag_ty = Type.@"null", .tag_ty_inferred = true, .fields = .{}, .values = .{}, @@ -17867,13 +17868,13 @@ const TypedValueAndOffset = struct { }; const ComptimePtrLoadKit = struct { - /// The Value and Type corresponding to the target of the provided pointer. + /// The Value and Type corresponding to the pointee of the provided pointer. /// If a direct dereference is not possible, this is null. - target: ?TypedValue, - /// The largest parent Value containing `target` and having a well-defined memory layout. - /// This is used for bitcasting, if direct dereferencing failed (i.e. `target` is null). + pointee: ?TypedValue, + /// The largest parent Value containing `pointee` and having a well-defined memory layout. + /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null). parent: ?TypedValueAndOffset, - /// Whether the `target` could be mutated by further + /// Whether the `pointee` could be mutated by further /// semantic analysis and a copy must be performed. is_mutable: bool, /// If the root decl could not be used as `parent`, this is the type that @@ -17885,7 +17886,7 @@ const ComptimePtrLoadError = CompileError || error{ RuntimeLoad, }; -/// If `maybe_array_ty` is provided, it will be used to directly dereference an +/// If `maybe_array_ty` is provided, it will be used to directly dereference an /// .elem_ptr of type T to a value of [N]T, if necessary. fn beginComptimePtrLoad( sema: *Sema, @@ -17908,10 +17909,10 @@ fn beginComptimePtrLoad( const decl_tv = try decl.typedValue(); if (decl_tv.val.tag() == .variable) return error.RuntimeLoad; - const layout_defined = try sema.typeHasWellDefinedLayout(block, src, decl.ty); + const layout_defined = decl.ty.hasWellDefinedLayout(); break :blk ComptimePtrLoadKit{ .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, - .target = decl_tv, + .pointee = decl_tv, .is_mutable = is_mutable, .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, }; @@ -17923,7 +17924,7 @@ fn beginComptimePtrLoad( var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null); if (elem_ptr.index != 0) { - if (try sema.typeHasWellDefinedLayout(block, src, elem_ty)) { + if (elem_ty.hasWellDefinedLayout()) { if (deref.parent) |*parent| { // Update the byte offset (in-place) const elem_size = try sema.typeAbiSize(block, src, elem_ty); @@ -17938,17 +17939,17 @@ fn beginComptimePtrLoad( // If we're loading an elem_ptr that was derived from a different type // than the true type of the underlying decl, we cannot deref directly - const ty_matches = if (deref.target != null and deref.target.?.ty.isArrayLike()) x: { - const deref_elem_ty = deref.target.?.ty.childType(); + const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayLike()) x: { + const deref_elem_ty = deref.pointee.?.ty.childType(); break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; } else false; if (!ty_matches) { - deref.target = null; + deref.pointee = null; break :blk deref; } - var array_tv = deref.target.?; + var array_tv = deref.pointee.?; const check_len = array_tv.ty.arrayLenIncludingSentinel(); if (elem_ptr.index >= check_len) { // TODO have the deref include the decl so we can say "declared here" @@ -17959,10 +17960,10 @@ fn beginComptimePtrLoad( if (maybe_array_ty) |load_ty| { // It's possible that we're loading a [N]T, in which case we'd like to slice - // the target array directly from our parent array. + // the pointee array directly from our parent array. if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty)) { const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel()); - deref.target = if (elem_ptr.index + N <= check_len) TypedValue{ + deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ .ty = try Type.array(sema.arena, N, null, elem_ty), .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N), } else null; @@ -17970,7 +17971,7 @@ fn beginComptimePtrLoad( } } - deref.target = .{ + deref.pointee = .{ .ty = elem_ty, .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index), }; @@ -17983,7 +17984,7 @@ fn beginComptimePtrLoad( const field_ty = field_ptr.container_ty.structFieldType(field_index); var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty); - if (try sema.typeHasWellDefinedLayout(block, src, field_ptr.container_ty)) { + if (field_ptr.container_ty.hasWellDefinedLayout()) { if (deref.parent) |*parent| { // Update the byte offset (in-place) try sema.resolveTypeLayout(block, src, field_ptr.container_ty); @@ -17995,19 +17996,19 @@ fn beginComptimePtrLoad( deref.ty_without_well_defined_layout = field_ptr.container_ty; } - if (deref.target) |*tv| { + if (deref.pointee) |*tv| { const coerce_in_mem_ok = (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; if (coerce_in_mem_ok) { - deref.target = TypedValue{ + deref.pointee = TypedValue{ .ty = field_ty, .val = try tv.val.fieldValue(sema.arena, field_index), }; break :blk deref; } } - deref.target = null; + deref.pointee = null; break :blk deref; }, @@ -18028,7 +18029,7 @@ fn beginComptimePtrLoad( deref.ty_without_well_defined_layout = payload_ptr.container_ty; } - if (deref.target) |*tv| { + if (deref.pointee) |*tv| { const coerce_in_mem_ok = (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok; @@ -18042,7 +18043,7 @@ fn beginComptimePtrLoad( break :blk deref; } } - deref.target = null; + deref.pointee = null; break :blk deref; }, @@ -18060,7 +18061,7 @@ fn beginComptimePtrLoad( else => unreachable, }; - if (deref.target) |tv| { + if (deref.pointee) |tv| { if (deref.parent == null and tv.ty.hasWellDefinedLayout()) { deref.parent = .{ .tv = tv, .byte_offset = 0 }; } @@ -21157,7 +21158,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr else => |e| return e, }; - if (deref.target) |tv| { + if (deref.pointee) |tv| { const coerce_in_mem_ok = (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok; @@ -21176,13 +21177,13 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr // The type is not in-memory coercible or the direct dereference failed, so it must // be bitcast according to the pointer type we are performing the load through. - if (!(try sema.typeHasWellDefinedLayout(block, src, load_ty))) + if (!load_ty.hasWellDefinedLayout()) return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty}); const load_sz = try sema.typeAbiSize(block, src, load_ty); // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` - if (deref.target) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty)) + if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty)) return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0); // If that fails, try to bit-cast from the largest parent value with a well-defined layout @@ -21271,182 +21272,6 @@ 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. diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 08cfe4a258..94dacf61e8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2829,7 +2829,10 @@ pub const DeclGen = struct { // (void) payload is the same. break :blk parent_llvm_ptr; } - const llvm_pl_index = if (layout.tag_size == 0) 0 else @boolToInt(layout.tag_align >= layout.payload_align); + const llvm_pl_index = if (layout.tag_size == 0) + 0 + else + @boolToInt(layout.tag_align >= layout.payload_align); const indices: [2]*const llvm.Value = .{ llvm_u32.constInt(0, .False), llvm_u32.constInt(llvm_pl_index, .False), diff --git a/src/type.zig b/src/type.zig index 3f6e3ef282..2df7cc83d8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2210,9 +2210,6 @@ pub const Type = extern union { .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, @@ -2226,6 +2223,9 @@ pub const Type = extern union { .c_mut_pointer, .single_const_pointer_to_comptime_int, .enum_numbered, + .vector, + .optional_single_mut_pointer, + .optional_single_const_pointer, => true, .anyopaque, @@ -2267,9 +2267,12 @@ pub const Type = extern union { .mut_slice, .enum_simple, .error_union, + .anyerror_void_error_union, .anyframe_T, .tuple, .anon_struct, + .empty_struct_literal, + .empty_struct, => false, .enum_full, @@ -2283,36 +2286,12 @@ pub const Type = extern union { .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, - } - }, + .optional => ty.isPtrLikeOptional(), + .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto, + .@"union" => ty.castTag(.@"union").?.data.layout != .Auto, + .union_tagged => false, }; } @@ -3299,13 +3278,12 @@ pub const Type = extern union { => return true, .optional => { - var buf: Payload.ElemType = undefined; - const child_type = self.optionalChild(&buf); + const child_ty = self.castTag(.optional).?.data; // optionals of zero sized types behave like bools, not pointers - if (!child_type.hasRuntimeBits()) return false; - if (child_type.zigTypeTag() != .Pointer) return false; + if (!child_ty.hasRuntimeBits()) return false; + if (child_ty.zigTypeTag() != .Pointer) return false; - const info = child_type.ptrInfo().data; + const info = child_ty.ptrInfo().data; switch (info.size) { .Slice, .C => return false, .Many, .One => return !info.@"allowzero", @@ -5496,6 +5474,7 @@ pub const Type = extern union { pub const @"type" = initTag(.type); pub const @"anyerror" = initTag(.anyerror); pub const @"anyopaque" = initTag(.anyopaque); + pub const @"null" = initTag(.@"null"); pub fn ptr(arena: Allocator, target: Target, data: Payload.Pointer.Data) !Type { var d = data; diff --git a/src/value.zig b/src/value.zig index af5ee75737..24cec0396e 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2417,7 +2417,7 @@ pub const Value = extern union { return switch (val.tag()) { .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array), .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]), - .array => Tag.array.create(arena, val.castTag(.array).?.data[start..end]), + .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]), .slice => sliceArray(val.castTag(.slice).?.data.ptr, arena, start, end), .decl_ref => sliceArray(val.castTag(.decl_ref).?.data.val, arena, start, end), @@ -2466,7 +2466,7 @@ pub const Value = extern union { pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize) Allocator.Error!Value { const elem_ty = ty.elemType2(); const ptr_val = switch (val.tag()) { - .slice => val.slicePtr(), + .slice => val.castTag(.slice).?.data.ptr, else => val, }; |
