authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-07-30 04:04:06+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-07-30 04:04:06+01:00
log8414638fb878febc9da2e956696d591c26fed101
treebf44274117109e55f2ee9cdf7f05b7d5b4c95ca0
parent3fbdd58a874c6b4dae84bed2ed31c945ff4adb54
parent08f1d63be1baf18ec00514204d49cb77b35115ba
signaturebadge-check Signed by PGP key B5690EEEBB952194

Sema: fixes (#24617)

* Sema: remove redundant comptime-known initializer tracking This logic predates certain Sema enhancements whose behavior it essentially tries to emulate in one specific case in a problematic way. In particular, this logic handled initializing comptime-known `const`s through RLS, which was reworked a few years back in 644041b to not rely on this logic, and catching runtime fields in comptime-only initializers, which has since been *correctly* fixed with better checks in `Sema.storePtr2`. That made the highly complex logic in `validateStructInit`, `validateUnionInit`, and `zirValidatePtrArrayInit` entirely redundant. Worse, it was also causing some tracked bugs, as well as a bug which I have identified and fixed in this PR (a corresponding behavior test is added). This commit simplifies union initialization by bringing the runtime logic more in line with the comptime logic: the tag is now always populated by `Sema.unionFieldPtr` based on `initializing`, where this previously happened only in the comptime case (with `validateUnionInit` instead handling it in the runtime case). Notably, this means that backends are now able to consider getting a pointer to an inactive union field as Illegal Behavior, because the `set_union_tag` instruction now appears *before* the `struct_field_ptr` instruction as you would probably expect it to. Resolves: #24520 Resolves: #24595 * Sema: fix comptime-known union initialization with OPV field The previous commit uncovered this existing OPV bug by triggering this logic more frequently. * Sema: remove dead logic This is redundant because `storePtr2` will coerce to the return type which (in `Sema.coerceInMemoryAllowedErrorSets`) will add errors to the current function's IES if necessary. * Sema: don't rely on Liveness We're currently experimenting with backends which effectively do their own liveness analysis, so this old trick of mine isn't necessarily valid anymore. However, we can fix that trivially: just make the "nop" instruction we jam into here have the right type. That way, the leftover field/element pointer instructions are perfectly valid, but still unused.

6 files changed, 138 insertions(+), 569 deletions(-)

lib/std/fmt.zig+2
......@@ -1101,6 +1101,8 @@ test "float.libc.sanity" {
11011101}
11021102
11031103test "union" {
1104 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1105
11041106 const TU = union(enum) {
11051107 float: f32,
11061108 int: u32,
src/Sema.zig+115-565
......@@ -3922,7 +3922,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
39223922 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
39233923 const ptr_to_map = switch (store_inst.tag) {
39243924 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.
3925 .set_union_tag => continue, // We can completely ignore these: we'll do it implicitly when we get the field pointer.
3925 .set_union_tag => continue, // Ignore for now; handled after we map pointers
39263926 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
39273927 else => unreachable,
39283928 };
......@@ -4055,19 +4055,33 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
40554055 }
40564056
40574057 // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.
4058 // Any implicit stores performed by `optional_payload_ptr_set`, `errunion_payload_ptr_set`, or
4059 // `set_union_tag` instructions were already done above.
4058 // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set`
4059 // instructions were already done above.
40604060
40614061 for (stores) |store_inst_idx| {
40624062 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
40634063 switch (store_inst.tag) {
4064 .set_union_tag => {}, // Handled implicitly by field pointers above
40654064 .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above
4065 .set_union_tag => {
4066 // Usually, we can ignore these, because the creation of the field pointer above
4067 // already did it for us. However, if the field is OPV, this is relevant, because
4068 // there is not going to be a store to the field. So we must initialize the union
4069 // tag if the field is OPV.
4070 const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
4071 const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?);
4072 const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?);
4073 const union_ty = union_ptr_val.typeOf(zcu).childType(zcu);
4074 const field_ty = union_ty.unionFieldType(tag_val, zcu).?;
4075 if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| {
4076 const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val);
4077 try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty);
4078 }
4079 },
40664080 .store, .store_safe => {
40674081 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
40684082 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?;
40694083 const new_ptr = ptr_mapping.get(air_ptr_inst).?;
4070 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(new_ptr), store_val, .fromInterned(zcu.intern_pool.typeOf(store_val.toIntern())));
4084 try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu));
40714085 },
40724086 else => unreachable,
40734087 }
......@@ -4099,14 +4113,13 @@ fn finishResolveComptimeKnownAllocPtr(
40994113 // We're almost done - we have the resolved comptime value. We just need to
41004114 // eliminate the now-dead runtime instructions.
41014115
4102 // We will rewrite the AIR to eliminate the alloc and all stores to it.
4103 // This will cause instructions deriving field pointers etc of the alloc to
4104 // become invalid, however, since we are removing all stores to those pointers,
4105 // they will be eliminated by Liveness before they reach codegen.
4106
4107 // The specifics of this instruction aren't really important: we just want
4108 // Liveness to elide it.
4109 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = .u8_type, .operand = .zero_u8 } } };
4116 // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to
4117 // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this
4118 // too because it doesn't really matter what they become.
4119 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{
4120 .ty = .fromIntern(alloc_ty.toIntern()),
4121 .operand = .zero_usize,
4122 } } };
41104123
41114124 sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);
41124125 for (comptime_info.stores.items(.inst)) |store_inst| {
......@@ -4829,13 +4842,13 @@ fn zirValidatePtrStructInit(
48294842 agg_ty,
48304843 init_src,
48314844 instrs,
4845 object_ptr,
48324846 ),
48334847 .@"union" => return sema.validateUnionInit(
48344848 block,
48354849 agg_ty,
48364850 init_src,
48374851 instrs,
4838 object_ptr,
48394852 ),
48404853 else => unreachable,
48414854 }
......@@ -4847,164 +4860,28 @@ fn validateUnionInit(
48474860 union_ty: Type,
48484861 init_src: LazySrcLoc,
48494862 instrs: []const Zir.Inst.Index,
4850 union_ptr: Air.Inst.Ref,
48514863) CompileError!void {
4852 const pt = sema.pt;
4853 const zcu = pt.zcu;
4854 const gpa = sema.gpa;
4855
4856 if (instrs.len != 1) {
4857 const msg = msg: {
4858 const msg = try sema.errMsg(
4859 init_src,
4860 "cannot initialize multiple union fields at once; unions can only have one active field",
4861 .{},
4862 );
4863 errdefer msg.destroy(gpa);
4864
4865 for (instrs[1..]) |inst| {
4866 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
4867 const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
4868 try sema.errNote(inst_src, msg, "additional initializer here", .{});
4869 }
4870 try sema.addDeclaredHereNote(msg, union_ty);
4871 break :msg msg;
4872 };
4873 return sema.failWithOwnedErrorMsg(block, msg);
4874 }
4875
4876 if (block.isComptime() and
4877 (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null)
4878 {
4879 // In this case, comptime machinery already did everything. No work to do here.
4864 if (instrs.len == 1) {
4865 // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`.
48804866 return;
48814867 }
4868 const msg = msg: {
4869 const msg = try sema.errMsg(
4870 init_src,
4871 "cannot initialize multiple union fields at once; unions can only have one active field",
4872 .{},
4873 );
4874 errdefer msg.destroy(sema.gpa);
48824875
4883 const field_ptr = instrs[0];
4884 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
4885 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
4886 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
4887 const field_name = try zcu.intern_pool.getOrPutString(
4888 gpa,
4889 pt.tid,
4890 sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
4891 .no_embedded_nulls,
4892 );
4893 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
4894 const air_tags = sema.air_instructions.items(.tag);
4895 const air_datas = sema.air_instructions.items(.data);
4896 const field_ptr_ref = sema.inst_map.get(field_ptr).?;
4897
4898 // Our task here is to determine if the union is comptime-known. In such case,
4899 // we erase the runtime AIR instructions for initializing the union, and replace
4900 // the mapping with the comptime value. Either way, we will need to populate the tag.
4901
4902 // We expect to see something like this in the current block AIR:
4903 // %a = alloc(*const U)
4904 // %b = bitcast(*U, %a)
4905 // %c = field_ptr(..., %b)
4906 // %e!= store(%c!, %d!)
4907 // If %d is a comptime operand, the union is comptime.
4908 // If the union is comptime, we want `first_block_index`
4909 // to point at %c so that the bitcast becomes the last instruction in the block.
4910 //
4911 // Store instruction may be missing; if field type has only one possible value, this case is handled below.
4912 //
4913 // In the case of a comptime-known pointer to a union, the
4914 // the field_ptr instruction is missing, so we have to pattern-match
4915 // based only on the store instructions.
4916 // `first_block_index` needs to point to the `field_ptr` if it exists;
4917 // the `store` otherwise.
4918 var first_block_index = block.instructions.items.len;
4919 var block_index = block.instructions.items.len - 1;
4920 var init_val: ?Value = null;
4921 var init_ref: ?Air.Inst.Ref = null;
4922 while (block_index > 0) : (block_index -= 1) {
4923 const store_inst = block.instructions.items[block_index];
4924 if (store_inst.toRef() == field_ptr_ref) {
4925 first_block_index = block_index;
4926 break;
4876 for (instrs[1..]) |inst| {
4877 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
4878 const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
4879 try sema.errNote(inst_src, msg, "additional initializer here", .{});
49274880 }
4928 switch (air_tags[@intFromEnum(store_inst)]) {
4929 .store, .store_safe => {},
4930 else => continue,
4931 }
4932 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
4933 var ptr_ref = bin_op.lhs;
4934 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
4935 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
4936 };
4937 if (ptr_ref != field_ptr_ref) continue;
4938 first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst|
4939 std.mem.lastIndexOfScalar(
4940 Air.Inst.Index,
4941 block.instructions.items[0..block_index],
4942 field_ptr_inst,
4943 ).?
4944 else
4945 block_index, first_block_index);
4946 init_ref = bin_op.rhs;
4947 init_val = try sema.resolveValue(bin_op.rhs);
4948 break;
4949 }
4950
4951 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
4952 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
4953 const field_type = union_ty.unionFieldType(tag_val, zcu).?;
4954
4955 if (try sema.typeHasOnePossibleValue(field_type)) |field_only_value| {
4956 init_val = field_only_value;
4957 }
4958
4959 if (init_val) |val| {
4960 // Our task is to delete all the `field_ptr` and `store` instructions, and insert
4961 // instead a single `store` to the result ptr with a comptime union value.
4962 block_index = first_block_index;
4963 for (block.instructions.items[first_block_index..]) |cur_inst| {
4964 switch (air_tags[@intFromEnum(cur_inst)]) {
4965 .struct_field_ptr,
4966 .struct_field_ptr_index_0,
4967 .struct_field_ptr_index_1,
4968 .struct_field_ptr_index_2,
4969 .struct_field_ptr_index_3,
4970 => if (cur_inst.toRef() == field_ptr_ref) continue,
4971 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue,
4972 .store, .store_safe => {
4973 var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
4974 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
4975 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
4976 };
4977 if (ptr_ref == field_ptr_ref) continue;
4978 },
4979 else => {},
4980 }
4981 block.instructions.items[block_index] = cur_inst;
4982 block_index += 1;
4983 }
4984 block.instructions.shrinkRetainingCapacity(block_index);
4985
4986 const union_val = try pt.internUnion(.{
4987 .ty = union_ty.toIntern(),
4988 .tag = tag_val.toIntern(),
4989 .val = val.toIntern(),
4990 });
4991 const union_init = Air.internedToRef(union_val);
4992 try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store);
4993 return;
4994 } else if (try union_ty.comptimeOnlySema(pt)) {
4995 const src = block.nodeOffset(field_ptr_data.src_node);
4996 return sema.failWithNeededComptime(block, src, .{ .comptime_only = .{
4997 .ty = union_ty,
4998 .msg = .union_init,
4999 } });
5000 }
5001 if (init_ref) |v| try sema.validateRuntimeValue(block, block.nodeOffset(field_ptr_data.src_node), v);
5002
5003 if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) {
5004 const new_tag = Air.internedToRef(tag_val.toIntern());
5005 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
5006 try sema.checkComptimeKnownStore(block, set_tag_inst, LazySrcLoc.unneeded); // `unneeded` since this isn't a "proper" store
5007 }
4881 try sema.addDeclaredHereNote(msg, union_ty);
4882 break :msg msg;
4883 };
4884 return sema.failWithOwnedErrorMsg(block, msg);
50084885}
50094886
50104887fn validateStructInit(
......@@ -5013,187 +4890,62 @@ fn validateStructInit(
50134890 struct_ty: Type,
50144891 init_src: LazySrcLoc,
50154892 instrs: []const Zir.Inst.Index,
4893 struct_ptr: Air.Inst.Ref,
50164894) CompileError!void {
50174895 const pt = sema.pt;
50184896 const zcu = pt.zcu;
50194897 const gpa = sema.gpa;
50204898 const ip = &zcu.intern_pool;
50214899
5022 const field_indices = try gpa.alloc(u32, instrs.len);
5023 defer gpa.free(field_indices);
5024
5025 // Maps field index to field_ptr index of where it was already initialized.
5026 const found_fields = try gpa.alloc(Zir.Inst.OptionalIndex, struct_ty.structFieldCount(zcu));
4900 // Tracks whether each field was explicitly initialized.
4901 const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu));
50274902 defer gpa.free(found_fields);
5028 @memset(found_fields, .none);
4903 @memset(found_fields, false);
50294904
5030 var struct_ptr_zir_ref: Zir.Inst.Ref = undefined;
5031
5032 for (instrs, field_indices) |field_ptr, *field_index| {
4905 for (instrs) |field_ptr| {
50334906 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
50344907 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
50354908 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
5036 struct_ptr_zir_ref = field_ptr_extra.lhs;
50374909 const field_name = try ip.getOrPutString(
50384910 gpa,
50394911 pt.tid,
50404912 sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
50414913 .no_embedded_nulls,
50424914 );
5043 field_index.* = if (struct_ty.isTuple(zcu))
4915 const field_index = if (struct_ty.isTuple(zcu))
50444916 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
50454917 else
50464918 try sema.structFieldIndex(block, struct_ty, field_name, field_src);
5047 assert(found_fields[field_index.*] == .none);
5048 found_fields[field_index.*] = field_ptr.toOptional();
4919 assert(found_fields[field_index] == false);
4920 found_fields[field_index] = true;
50494921 }
50504922
5051 var root_msg: ?*Zcu.ErrorMsg = null;
5052 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
4923 // Our job is simply to deal with default field values. Specifically, any field which was not
4924 // explicitly initialized must have its default value stored to the field pointer, or, if the
4925 // field has no default value, a compile error must be emitted instead.
50534926
5054 const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
5055 if (block.isComptime() and
5056 (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
5057 {
5058 try struct_ty.resolveLayout(pt);
5059 // In this case the only thing we need to do is evaluate the implicit
5060 // store instructions for default field values, and report any missing fields.
5061 // Avoid the cost of the extra machinery for detecting a comptime struct init value.
5062 for (found_fields, 0..) |field_ptr, i_usize| {
5063 const i: u32 = @intCast(i_usize);
5064 if (field_ptr != .none) continue;
5065
5066 try struct_ty.resolveStructFieldInits(pt);
5067 const default_val = struct_ty.structFieldDefaultValue(i, zcu);
5068 if (default_val.toIntern() == .unreachable_value) {
5069 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
5070 const template = "missing tuple field with index {d}";
5071 if (root_msg) |msg| {
5072 try sema.errNote(init_src, msg, template, .{i});
5073 } else {
5074 root_msg = try sema.errMsg(init_src, template, .{i});
5075 }
5076 continue;
5077 };
5078 const template = "missing struct field: {f}";
5079 const args = .{field_name.fmt(ip)};
5080 if (root_msg) |msg| {
5081 try sema.errNote(init_src, msg, template, args);
5082 } else {
5083 root_msg = try sema.errMsg(init_src, template, args);
5084 }
5085 continue;
5086 }
5087
5088 const field_src = init_src; // TODO better source location
5089 const default_field_ptr = if (struct_ty.isTuple(zcu))
5090 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
5091 else
5092 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
5093 const init = Air.internedToRef(default_val.toIntern());
5094 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
5095 }
5096
5097 if (root_msg) |msg| {
5098 try sema.addDeclaredHereNote(msg, struct_ty);
5099 root_msg = null;
5100 return sema.failWithOwnedErrorMsg(block, msg);
5101 }
5102
5103 return;
5104 }
5105
5106 var fields_allow_runtime = true;
5107
5108 var struct_is_comptime = true;
5109 var first_block_index = block.instructions.items.len;
5110
5111 const require_comptime = try struct_ty.comptimeOnlySema(pt);
5112 const air_tags = sema.air_instructions.items(.tag);
5113 const air_datas = sema.air_instructions.items(.data);
5114
5115 try struct_ty.resolveStructFieldInits(pt);
4927 // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However,
4928 // that work was actually all redundant:
4929 //
4930 // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing
4931 // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known
4932 // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends.
4933 //
4934 // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known.
4935 // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the
4936 // field stores were to comptime-mutable pointers, so have already errored if not comptime-known.
4937 //
4938 // * If the value is runtime-known, then comptime-known fields must be validated as runtime values.
4939 // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`.
51164940
5117 // We collect the comptime field values in case the struct initialization
5118 // ends up being comptime-known.
5119 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(zcu));
4941 var root_msg: ?*Zcu.ErrorMsg = null;
4942 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
51204943
5121 field: for (found_fields, 0..) |opt_field_ptr, i_usize| {
4944 for (found_fields, 0..) |explicit, i_usize| {
4945 if (explicit) continue;
51224946 const i: u32 = @intCast(i_usize);
5123 if (opt_field_ptr.unwrap()) |field_ptr| {
5124 // Determine whether the value stored to this pointer is comptime-known.
5125 const field_ty = struct_ty.fieldType(i, zcu);
5126 if (try sema.typeHasOnePossibleValue(field_ty)) |opv| {
5127 field_values[i] = opv.toIntern();
5128 continue;
5129 }
5130
5131 const field_ptr_ref = sema.inst_map.get(field_ptr).?;
5132
5133 //std.debug.print("validateStructInit (field_ptr_ref=%{d}):\n", .{field_ptr_ref});
5134 //for (block.instructions.items) |item| {
5135 // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[@intFromEnum(item)])});
5136 //}
5137
5138 // We expect to see something like this in the current block AIR:
5139 // %a = field_ptr(...)
5140 // store(%a, %b)
5141 // With an optional bitcast between the store and the field_ptr.
5142 // If %b is a comptime operand, this field is comptime.
5143 //
5144 // However, in the case of a comptime-known pointer to a struct, the
5145 // the field_ptr instruction is missing, so we have to pattern-match
5146 // based only on the store instructions.
5147 // `first_block_index` needs to point to the `field_ptr` if it exists;
5148 // the `store` otherwise.
5149
5150 // Possible performance enhancement: save the `block_index` between iterations
5151 // of the for loop.
5152 var block_index = block.instructions.items.len;
5153 while (block_index > 0) {
5154 block_index -= 1;
5155 const store_inst = block.instructions.items[block_index];
5156 if (store_inst.toRef() == field_ptr_ref) {
5157 struct_is_comptime = false;
5158 continue :field;
5159 }
5160 switch (air_tags[@intFromEnum(store_inst)]) {
5161 .store, .store_safe => {},
5162 else => continue,
5163 }
5164 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
5165 var ptr_ref = bin_op.lhs;
5166 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
5167 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
5168 };
5169 if (ptr_ref != field_ptr_ref) continue;
5170 first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst|
5171 std.mem.lastIndexOfScalar(
5172 Air.Inst.Index,
5173 block.instructions.items[0..block_index],
5174 field_ptr_inst,
5175 ).?
5176 else
5177 block_index, first_block_index);
5178 if (!sema.checkRuntimeValue(bin_op.rhs)) fields_allow_runtime = false;
5179 if (try sema.resolveValue(bin_op.rhs)) |val| {
5180 field_values[i] = val.toIntern();
5181 } else if (require_comptime) {
5182 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
5183 const src = block.nodeOffset(field_ptr_data.src_node);
5184 return sema.failWithNeededComptime(block, src, .{ .comptime_only = .{
5185 .ty = struct_ty,
5186 .msg = .struct_init,
5187 } });
5188 } else {
5189 struct_is_comptime = false;
5190 }
5191 continue :field;
5192 }
5193 struct_is_comptime = false;
5194 continue :field;
5195 }
51964947
4948 try struct_ty.resolveStructFieldInits(pt);
51974949 const default_val = struct_ty.structFieldDefaultValue(i, zcu);
51984950 if (default_val.toIntern() == .unreachable_value) {
51994951 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
......@@ -5214,70 +4966,6 @@ fn validateStructInit(
52144966 }
52154967 continue;
52164968 }
5217 field_values[i] = default_val.toIntern();
5218 }
5219
5220 if (!struct_is_comptime and !fields_allow_runtime and root_msg == null) {
5221 root_msg = try sema.errMsg(init_src, "runtime value contains reference to comptime var", .{});
5222 try sema.errNote(init_src, root_msg.?, "comptime var pointers are not available at runtime", .{});
5223 }
5224
5225 if (root_msg) |msg| {
5226 try sema.addDeclaredHereNote(msg, struct_ty);
5227 root_msg = null;
5228 return sema.failWithOwnedErrorMsg(block, msg);
5229 }
5230
5231 if (struct_is_comptime) {
5232 // Our task is to delete all the `field_ptr` and `store` instructions, and insert
5233 // instead a single `store` to the struct_ptr with a comptime struct value.
5234 var init_index: usize = 0;
5235 var field_ptr_ref = Air.Inst.Ref.none;
5236 var block_index = first_block_index;
5237 for (block.instructions.items[first_block_index..]) |cur_inst| {
5238 while (field_ptr_ref == .none and init_index < instrs.len) : (init_index += 1) {
5239 const field_ty = struct_ty.fieldType(field_indices[init_index], zcu);
5240 if (try field_ty.onePossibleValue(pt)) |_| continue;
5241 field_ptr_ref = sema.inst_map.get(instrs[init_index]).?;
5242 }
5243 switch (air_tags[@intFromEnum(cur_inst)]) {
5244 .struct_field_ptr,
5245 .struct_field_ptr_index_0,
5246 .struct_field_ptr_index_1,
5247 .struct_field_ptr_index_2,
5248 .struct_field_ptr_index_3,
5249 => if (cur_inst.toRef() == field_ptr_ref) continue,
5250 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue,
5251 .store, .store_safe => {
5252 var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
5253 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
5254 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
5255 };
5256 if (ptr_ref == field_ptr_ref) {
5257 field_ptr_ref = .none;
5258 continue;
5259 }
5260 },
5261 else => {},
5262 }
5263 block.instructions.items[block_index] = cur_inst;
5264 block_index += 1;
5265 }
5266 block.instructions.shrinkRetainingCapacity(block_index);
5267
5268 const struct_val = try pt.intern(.{ .aggregate = .{
5269 .ty = struct_ty.toIntern(),
5270 .storage = .{ .elems = field_values },
5271 } });
5272 const struct_init = Air.internedToRef(struct_val);
5273 try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
5274 return;
5275 }
5276 try struct_ty.resolveLayout(pt);
5277
5278 // Our task is to insert `store` instructions for all the default field values.
5279 for (found_fields, 0..) |field_ptr, i| {
5280 if (field_ptr != .none) continue;
52814969
52824970 const field_src = init_src; // TODO better source location
52834971 const default_field_ptr = if (struct_ty.isTuple(zcu))
......@@ -5285,8 +4973,13 @@ fn validateStructInit(
52854973 else
52864974 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
52874975 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
5288 const init = Air.internedToRef(field_values[i]);
5289 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
4976 try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
4977 }
4978
4979 if (root_msg) |msg| {
4980 try sema.addDeclaredHereNote(msg, struct_ty);
4981 root_msg = null;
4982 return sema.failWithOwnedErrorMsg(block, msg);
52904983 }
52914984}
52924985
......@@ -5307,15 +5000,14 @@ fn zirValidatePtrArrayInit(
53075000 const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);
53085001 const array_len = array_ty.arrayLen(zcu);
53095002
5310 // Collect the comptime element values in case the array literal ends up
5311 // being comptime-known.
5312 const element_vals = try sema.arena.alloc(
5313 InternPool.Index,
5314 try sema.usizeCast(block, init_src, array_len),
5315 );
5003 // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR
5004 // to initialize them, or emitting a compile error if an unspecified field has no default. For
5005 // tuples, there are literally default field values, although they're guaranteed to be comptime
5006 // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never
5007 // specified so we always need to initialize here. For vectors, there's no such thing.
53165008
5317 if (instrs.len != array_len) switch (array_ty.zigTypeTag(zcu)) {
5318 .@"struct" => {
5009 switch (array_ty.zigTypeTag(zcu)) {
5010 .@"struct" => if (instrs.len != array_len) {
53195011 var root_msg: ?*Zcu.ErrorMsg = null;
53205012 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
53215013
......@@ -5332,8 +5024,6 @@ fn zirValidatePtrArrayInit(
53325024 }
53335025 continue;
53345026 }
5335
5336 element_vals[i] = default_val;
53375027 }
53385028
53395029 if (root_msg) |msg| {
......@@ -5341,162 +5031,25 @@ fn zirValidatePtrArrayInit(
53415031 return sema.failWithOwnedErrorMsg(block, msg);
53425032 }
53435033 },
5344 .array => {
5034
5035 .array => if (instrs.len != array_len) {
53455036 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
53465037 array_len, instrs.len,
53475038 });
5039 } else if (array_ty.sentinel(zcu)) |sentinel| {
5040 const array_len_ref = try pt.intRef(.usize, array_len);
5041 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
5042 try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr);
5043 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store);
53485044 },
5349 .vector => {
5045
5046 .vector => if (instrs.len != array_len) {
53505047 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
53515048 array_len, instrs.len,
53525049 });
53535050 },
5354 else => unreachable,
5355 };
5356
5357 if (block.isComptime() and
5358 (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null)
5359 {
5360 // In this case the comptime machinery will have evaluated the store instructions
5361 // at comptime so we have almost nothing to do here. However, in case of a
5362 // sentinel-terminated array, the sentinel will not have been populated by
5363 // any ZIR instructions at comptime; we need to do that here.
5364 if (array_ty.sentinel(zcu)) |sentinel_val| {
5365 const array_len_ref = try pt.intRef(.usize, array_len);
5366 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
5367 const sentinel = Air.internedToRef(sentinel_val.toIntern());
5368 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
5369 }
5370 return;
5371 }
5372
5373 // If the array has one possible value, the value is always comptime-known.
5374 if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| {
5375 const array_init = Air.internedToRef(array_opv.toIntern());
5376 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
5377 return;
5378 }
5379
5380 var array_is_comptime = true;
5381 var first_block_index = block.instructions.items.len;
53825051
5383 const air_tags = sema.air_instructions.items(.tag);
5384 const air_datas = sema.air_instructions.items(.data);
5385
5386 outer: for (instrs, 0..) |elem_ptr, i| {
5387 // Determine whether the value stored to this pointer is comptime-known.
5388
5389 if (array_ty.isTuple(zcu)) {
5390 if (array_ty.structFieldIsComptime(i, zcu))
5391 try array_ty.resolveStructFieldInits(pt);
5392 if (try array_ty.structFieldValueComptime(pt, i)) |opv| {
5393 element_vals[i] = opv.toIntern();
5394 continue;
5395 }
5396 }
5397
5398 const elem_ptr_ref = sema.inst_map.get(elem_ptr).?;
5399
5400 // We expect to see something like this in the current block AIR:
5401 // %a = elem_ptr(...)
5402 // store(%a, %b)
5403 // With an optional bitcast between the store and the elem_ptr.
5404 // If %b is a comptime operand, this element is comptime.
5405 //
5406 // However, in the case of a comptime-known pointer to an array, the
5407 // the elem_ptr instruction is missing, so we have to pattern-match
5408 // based only on the store instructions.
5409 // `first_block_index` needs to point to the `elem_ptr` if it exists;
5410 // the `store` otherwise.
5411 //
5412 // This is nearly identical to similar logic in `validateStructInit`.
5413
5414 // Possible performance enhancement: save the `block_index` between iterations
5415 // of the for loop.
5416 var block_index = block.instructions.items.len;
5417 while (block_index > 0) {
5418 block_index -= 1;
5419 const store_inst = block.instructions.items[block_index];
5420 if (store_inst.toRef() == elem_ptr_ref) {
5421 array_is_comptime = false;
5422 continue :outer;
5423 }
5424 switch (air_tags[@intFromEnum(store_inst)]) {
5425 .store, .store_safe => {},
5426 else => continue,
5427 }
5428 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
5429 var ptr_ref = bin_op.lhs;
5430 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
5431 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
5432 };
5433 if (ptr_ref != elem_ptr_ref) continue;
5434 first_block_index = @min(if (elem_ptr_ref.toIndex()) |elem_ptr_inst|
5435 std.mem.lastIndexOfScalar(
5436 Air.Inst.Index,
5437 block.instructions.items[0..block_index],
5438 elem_ptr_inst,
5439 ).?
5440 else
5441 block_index, first_block_index);
5442 if (try sema.resolveValue(bin_op.rhs)) |val| {
5443 element_vals[i] = val.toIntern();
5444 } else {
5445 array_is_comptime = false;
5446 }
5447 continue :outer;
5448 }
5449 array_is_comptime = false;
5450 continue :outer;
5451 }
5452
5453 if (array_is_comptime) {
5454 if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| {
5455 switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
5456 .ptr => |ptr| switch (ptr.base_addr) {
5457 .comptime_field => return, // This store was validated by the individual elem ptrs.
5458 else => {},
5459 },
5460 else => {},
5461 }
5462 }
5463
5464 // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
5465 // instead a single `store` to the array_ptr with a comptime struct value.
5466 var elem_index: usize = 0;
5467 var elem_ptr_ref = Air.Inst.Ref.none;
5468 var block_index = first_block_index;
5469 for (block.instructions.items[first_block_index..]) |cur_inst| {
5470 while (elem_ptr_ref == .none and elem_index < instrs.len) : (elem_index += 1) {
5471 if (array_ty.isTuple(zcu) and array_ty.structFieldIsComptime(elem_index, zcu)) continue;
5472 elem_ptr_ref = sema.inst_map.get(instrs[elem_index]).?;
5473 }
5474 switch (air_tags[@intFromEnum(cur_inst)]) {
5475 .ptr_elem_ptr => if (cur_inst.toRef() == elem_ptr_ref) continue,
5476 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == elem_ptr_ref) continue,
5477 .store, .store_safe => {
5478 var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
5479 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
5480 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
5481 };
5482 if (ptr_ref == elem_ptr_ref) {
5483 elem_ptr_ref = .none;
5484 continue;
5485 }
5486 },
5487 else => {},
5488 }
5489 block.instructions.items[block_index] = cur_inst;
5490 block_index += 1;
5491 }
5492 block.instructions.shrinkRetainingCapacity(block_index);
5493
5494 const array_val = try pt.intern(.{ .aggregate = .{
5495 .ty = array_ty.toIntern(),
5496 .storage = .{ .elems = element_vals },
5497 } });
5498 const array_init = Air.internedToRef(array_val);
5499 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
5052 else => unreachable,
55005053 }
55015054}
55025055
......@@ -5774,8 +5327,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
57745327 const tracy = trace(@src());
57755328 defer tracy.end();
57765329
5777 const pt = sema.pt;
5778 const zcu = pt.zcu;
57795330 const zir_tags = sema.code.instructions.items(.tag);
57805331 const zir_datas = sema.code.instructions.items(.data);
57815332 const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
......@@ -5789,16 +5340,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
57895340 else
57905341 false;
57915342
5792 // Check for the possibility of this pattern:
5793 // %a = ret_ptr
5794 // %b = store(%a, %c)
5795 // Where %c is an error union or error set. In such case we need to add
5796 // to the current function's inferred error set, if any.
5797 if (is_ret and sema.fn_ret_ty_ies != null) switch (sema.typeOf(operand).zigTypeTag(zcu)) {
5798 .error_union, .error_set => try sema.addToInferredErrorSet(operand),
5799 else => {},
5800 };
5801
58025343 const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
58035344 const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });
58045345 const air_tag: Air.Inst.Tag = if (is_ret)
......@@ -28015,15 +27556,24 @@ fn unionFieldPtr(
2801527556 return Air.internedToRef(field_ptr_val.toIntern());
2801627557 }
2801727558
28018 if (!initializing and union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and
28019 union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1)
28020 {
28021 const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
28022 const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
28023 // TODO would it be better if get_union_tag supported pointers to unions?
28024 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
28025 const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_val);
28026 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag);
27559 // If the union has a tag, we must either set or or safety check it depending on `initializing`.
27560 tag: {
27561 if (union_ty.containerLayout(zcu) != .auto) break :tag;
27562 const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty);
27563 if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag;
27564 // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
27565 // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
27566 const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
27567 if (initializing) {
27568 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
27569 try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
27570 } else if (block.wantSafety() and union_obj.hasTag(ip)) {
27571 // The tag exists at runtime (safety tag), so emit a safety check.
27572 // TODO would it be better if get_union_tag supported pointers to unions?
27573 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
27574 const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val);
27575 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag));
27576 }
2802727577 }
2802827578 if (field_ty.zigTypeTag(zcu) == .noreturn) {
2802927579 _ = try block.addNoOp(.unreach);
test/behavior/array.zig+13-1
......@@ -540,7 +540,6 @@ test "sentinel element count towards the ABI size calculation" {
540540}
541541
542542test "zero-sized array with recursive type definition" {
543 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
544543 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
545544 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
546545
......@@ -1098,3 +1097,16 @@ test "initialize pointer to anyopaque with reference to empty array initializer"
10981097 // We can't check the value, but it's zero-bit, so the type matching is good enough.
10991098 comptime assert(@TypeOf(loaded) == @TypeOf(.{}));
11001099}
1100
1101test "sentinel of runtime-known array initialization is populated" {
1102 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1103
1104 var rt: u32 = undefined;
1105 rt = 42;
1106
1107 const arr: [1:123]u32 = .{rt};
1108 const elems: [*]const u32 = &arr;
1109
1110 try expect(elems[0] == 42);
1111 try expect(elems[1] == 123);
1112}
test/behavior/cast_int.zig+1
......@@ -217,6 +217,7 @@ test "load non byte-sized value in struct" {
217217test "load non byte-sized value in union" {
218218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
219219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
220 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
220221 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
221222 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
222223
test/behavior/field_parent_ptr.zig+6-3
......@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
22const builtin = @import("builtin");
33
44test "@fieldParentPtr struct" {
5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
87
......@@ -587,10 +586,12 @@ test "@fieldParentPtr extern struct last zero-bit field" {
587586}
588587
589588test "@fieldParentPtr unaligned packed struct" {
589 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
590590 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
591591 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
592592 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
593593 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
594 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
594595
595596 const C = packed struct {
596597 a: bool = true,
......@@ -725,10 +726,12 @@ test "@fieldParentPtr unaligned packed struct" {
725726}
726727
727728test "@fieldParentPtr aligned packed struct" {
729 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
728730 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
729731 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
730732 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
731733 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
734 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
732735
733736 const C = packed struct {
734737 a: f32 = 3.14,
......@@ -866,6 +869,7 @@ test "@fieldParentPtr nested packed struct" {
866869 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
867870 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
868871 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
872 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
869873
870874 {
871875 const C = packed struct {
......@@ -1340,7 +1344,6 @@ test "@fieldParentPtr packed struct last zero-bit field" {
13401344}
13411345
13421346test "@fieldParentPtr tagged union" {
1343 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
13441347 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13451348 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
13461349
......@@ -1477,7 +1480,6 @@ test "@fieldParentPtr tagged union" {
14771480}
14781481
14791482test "@fieldParentPtr untagged union" {
1480 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
14811483 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
14821484 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
14831485
......@@ -1614,6 +1616,7 @@ test "@fieldParentPtr untagged union" {
16141616}
16151617
16161618test "@fieldParentPtr extern union" {
1619 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
16171620 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
16181621 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
16191622
test/behavior/packed-struct.zig+1
......@@ -1319,6 +1319,7 @@ test "packed struct equality ignores padding bits" {
13191319}
13201320
13211321test "packed struct with signed field" {
1322 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
13221323 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
13231324
13241325 var s: packed struct {