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" {...@@ -1101,6 +1101,8 @@ test "float.libc.sanity" {
1101}1101}
11021102
1103test "union" {1103test "union" {
1104 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1105
1104 const TU = union(enum) {1106 const TU = union(enum) {
1105 float: f32,1107 float: f32,
1106 int: u32,1108 int: u32,
src/Sema.zig+115-565
...@@ -3922,7 +3922,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,...@@ -3922,7 +3922,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
3922 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));3922 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
3923 const ptr_to_map = switch (store_inst.tag) {3923 const ptr_to_map = switch (store_inst.tag) {
3924 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.3924 .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
3926 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.3926 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
3927 else => unreachable,3927 else => unreachable,
3928 };3928 };
...@@ -4055,19 +4055,33 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,...@@ -4055,19 +4055,33 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
4055 }4055 }
40564056
4057 // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.4057 // 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`, or4058 // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set`
4059 // `set_union_tag` instructions were already done above.4059 // instructions were already done above.
40604060
4061 for (stores) |store_inst_idx| {4061 for (stores) |store_inst_idx| {
4062 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));4062 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
4063 switch (store_inst.tag) {4063 switch (store_inst.tag) {
4064 .set_union_tag => {}, // Handled implicitly by field pointers above
4065 .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above4064 .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 },
4066 .store, .store_safe => {4080 .store, .store_safe => {
4067 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;4081 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
4068 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?;4082 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?;
4069 const new_ptr = ptr_mapping.get(air_ptr_inst).?;4083 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));
4071 },4085 },
4072 else => unreachable,4086 else => unreachable,
4073 }4087 }
...@@ -4099,14 +4113,13 @@ fn finishResolveComptimeKnownAllocPtr(...@@ -4099,14 +4113,13 @@ fn finishResolveComptimeKnownAllocPtr(
4099 // We're almost done - we have the resolved comptime value. We just need to4113 // We're almost done - we have the resolved comptime value. We just need to
4100 // eliminate the now-dead runtime instructions.4114 // eliminate the now-dead runtime instructions.
41014115
4102 // We will rewrite the AIR to eliminate the alloc and all stores to it.4116 // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to
4103 // This will cause instructions deriving field pointers etc of the alloc to4117 // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this
4104 // become invalid, however, since we are removing all stores to those pointers,4118 // too because it doesn't really matter what they become.
4105 // they will be eliminated by Liveness before they reach codegen.4119 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{
41064120 .ty = .fromIntern(alloc_ty.toIntern()),
4107 // The specifics of this instruction aren't really important: we just want4121 .operand = .zero_usize,
4108 // Liveness to elide it.4122 } } };
4109 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = .u8_type, .operand = .zero_u8 } } };
41104123
4111 sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);4124 sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);
4112 for (comptime_info.stores.items(.inst)) |store_inst| {4125 for (comptime_info.stores.items(.inst)) |store_inst| {
...@@ -4829,13 +4842,13 @@ fn zirValidatePtrStructInit(...@@ -4829,13 +4842,13 @@ fn zirValidatePtrStructInit(
4829 agg_ty,4842 agg_ty,
4830 init_src,4843 init_src,
4831 instrs,4844 instrs,
4845 object_ptr,
4832 ),4846 ),
4833 .@"union" => return sema.validateUnionInit(4847 .@"union" => return sema.validateUnionInit(
4834 block,4848 block,
4835 agg_ty,4849 agg_ty,
4836 init_src,4850 init_src,
4837 instrs,4851 instrs,
4838 object_ptr,
4839 ),4852 ),
4840 else => unreachable,4853 else => unreachable,
4841 }4854 }
...@@ -4847,164 +4860,28 @@ fn validateUnionInit(...@@ -4847,164 +4860,28 @@ fn validateUnionInit(
4847 union_ty: Type,4860 union_ty: Type,
4848 init_src: LazySrcLoc,4861 init_src: LazySrcLoc,
4849 instrs: []const Zir.Inst.Index,4862 instrs: []const Zir.Inst.Index,
4850 union_ptr: Air.Inst.Ref,
4851) CompileError!void {4863) CompileError!void {
4852 const pt = sema.pt;4864 if (instrs.len == 1) {
4853 const zcu = pt.zcu;4865 // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`.
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.
4880 return;4866 return;
4881 }4867 }
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];4876 for (instrs[1..]) |inst| {
4884 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;4877 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
4885 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });4878 const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
4886 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;4879 try sema.errNote(inst_src, msg, "additional initializer here", .{});
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;
4927 }4880 }
4928 switch (air_tags[@intFromEnum(store_inst)]) {4881 try sema.addDeclaredHereNote(msg, union_ty);
4929 .store, .store_safe => {},4882 break :msg msg;
4930 else => continue,4883 };
4931 }4884 return sema.failWithOwnedErrorMsg(block, msg);
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 }
5008}4885}
50094886
5010fn validateStructInit(4887fn validateStructInit(
...@@ -5013,187 +4890,62 @@ fn validateStructInit(...@@ -5013,187 +4890,62 @@ fn validateStructInit(
5013 struct_ty: Type,4890 struct_ty: Type,
5014 init_src: LazySrcLoc,4891 init_src: LazySrcLoc,
5015 instrs: []const Zir.Inst.Index,4892 instrs: []const Zir.Inst.Index,
4893 struct_ptr: Air.Inst.Ref,
5016) CompileError!void {4894) CompileError!void {
5017 const pt = sema.pt;4895 const pt = sema.pt;
5018 const zcu = pt.zcu;4896 const zcu = pt.zcu;
5019 const gpa = sema.gpa;4897 const gpa = sema.gpa;
5020 const ip = &zcu.intern_pool;4898 const ip = &zcu.intern_pool;
50214899
5022 const field_indices = try gpa.alloc(u32, instrs.len);4900 // Tracks whether each field was explicitly initialized.
5023 defer gpa.free(field_indices);4901 const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu));
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));
5027 defer gpa.free(found_fields);4902 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;4905 for (instrs) |field_ptr| {
5031
5032 for (instrs, field_indices) |field_ptr, *field_index| {
5033 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;4906 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
5034 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });4907 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
5035 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;4908 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;
5037 const field_name = try ip.getOrPutString(4909 const field_name = try ip.getOrPutString(
5038 gpa,4910 gpa,
5039 pt.tid,4911 pt.tid,
5040 sema.code.nullTerminatedString(field_ptr_extra.field_name_start),4912 sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
5041 .no_embedded_nulls,4913 .no_embedded_nulls,
5042 );4914 );
5043 field_index.* = if (struct_ty.isTuple(zcu))4915 const field_index = if (struct_ty.isTuple(zcu))
5044 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)4916 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
5045 else4917 else
5046 try sema.structFieldIndex(block, struct_ty, field_name, field_src);4918 try sema.structFieldIndex(block, struct_ty, field_name, field_src);
5047 assert(found_fields[field_index.*] == .none);4919 assert(found_fields[field_index] == false);
5048 found_fields[field_index.*] = field_ptr.toOptional();4920 found_fields[field_index] = true;
5049 }4921 }
50504922
5051 var root_msg: ?*Zcu.ErrorMsg = null;4923 // Our job is simply to deal with default field values. Specifically, any field which was not
5052 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);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);4927 // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However,
5055 if (block.isComptime() and4928 // that work was actually all redundant:
5056 (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)4929 //
5057 {4930 // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing
5058 try struct_ty.resolveLayout(pt);4931 // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known
5059 // In this case the only thing we need to do is evaluate the implicit4932 // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends.
5060 // store instructions for default field values, and report any missing fields.4933 //
5061 // Avoid the cost of the extra machinery for detecting a comptime struct init value.4934 // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known.
5062 for (found_fields, 0..) |field_ptr, i_usize| {4935 // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the
5063 const i: u32 = @intCast(i_usize);4936 // field stores were to comptime-mutable pointers, so have already errored if not comptime-known.
5064 if (field_ptr != .none) continue;4937 //
50654938 // * If the value is runtime-known, then comptime-known fields must be validated as runtime values.
5066 try struct_ty.resolveStructFieldInits(pt);4939 // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`.
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);
51164940
5117 // We collect the comptime field values in case the struct initialization4941 var root_msg: ?*Zcu.ErrorMsg = null;
5118 // ends up being comptime-known.4942 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
5119 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(zcu));
51204943
5121 field: for (found_fields, 0..) |opt_field_ptr, i_usize| {4944 for (found_fields, 0..) |explicit, i_usize| {
4945 if (explicit) continue;
5122 const i: u32 = @intCast(i_usize);4946 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);
5197 const default_val = struct_ty.structFieldDefaultValue(i, zcu);4949 const default_val = struct_ty.structFieldDefaultValue(i, zcu);
5198 if (default_val.toIntern() == .unreachable_value) {4950 if (default_val.toIntern() == .unreachable_value) {
5199 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {4951 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
...@@ -5214,70 +4966,6 @@ fn validateStructInit(...@@ -5214,70 +4966,6 @@ fn validateStructInit(
5214 }4966 }
5215 continue;4967 continue;
5216 }4968 }
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
5282 const field_src = init_src; // TODO better source location4970 const field_src = init_src; // TODO better source location
5283 const default_field_ptr = if (struct_ty.isTuple(zcu))4971 const default_field_ptr = if (struct_ty.isTuple(zcu))
...@@ -5285,8 +4973,13 @@ fn validateStructInit(...@@ -5285,8 +4973,13 @@ fn validateStructInit(
5285 else4973 else
5286 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);4974 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
5287 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);4975 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
5288 const init = Air.internedToRef(field_values[i]);4976 try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
5289 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, 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);
5290 }4983 }
5291}4984}
52924985
...@@ -5307,15 +5000,14 @@ fn zirValidatePtrArrayInit(...@@ -5307,15 +5000,14 @@ fn zirValidatePtrArrayInit(
5307 const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);5000 const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);
5308 const array_len = array_ty.arrayLen(zcu);5001 const array_len = array_ty.arrayLen(zcu);
53095002
5310 // Collect the comptime element values in case the array literal ends up5003 // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR
5311 // being comptime-known.5004 // to initialize them, or emitting a compile error if an unspecified field has no default. For
5312 const element_vals = try sema.arena.alloc(5005 // tuples, there are literally default field values, although they're guaranteed to be comptime
5313 InternPool.Index,5006 // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never
5314 try sema.usizeCast(block, init_src, array_len),5007 // specified so we always need to initialize here. For vectors, there's no such thing.
5315 );
53165008
5317 if (instrs.len != array_len) switch (array_ty.zigTypeTag(zcu)) {5009 switch (array_ty.zigTypeTag(zcu)) {
5318 .@"struct" => {5010 .@"struct" => if (instrs.len != array_len) {
5319 var root_msg: ?*Zcu.ErrorMsg = null;5011 var root_msg: ?*Zcu.ErrorMsg = null;
5320 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);5012 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
53215013
...@@ -5332,8 +5024,6 @@ fn zirValidatePtrArrayInit(...@@ -5332,8 +5024,6 @@ fn zirValidatePtrArrayInit(
5332 }5024 }
5333 continue;5025 continue;
5334 }5026 }
5335
5336 element_vals[i] = default_val;
5337 }5027 }
53385028
5339 if (root_msg) |msg| {5029 if (root_msg) |msg| {
...@@ -5341,162 +5031,25 @@ fn zirValidatePtrArrayInit(...@@ -5341,162 +5031,25 @@ fn zirValidatePtrArrayInit(
5341 return sema.failWithOwnedErrorMsg(block, msg);5031 return sema.failWithOwnedErrorMsg(block, msg);
5342 }5032 }
5343 },5033 },
5344 .array => {5034
5035 .array => if (instrs.len != array_len) {
5345 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{5036 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
5346 array_len, instrs.len,5037 array_len, instrs.len,
5347 });5038 });
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);
5348 },5044 },
5349 .vector => {5045
5046 .vector => if (instrs.len != array_len) {
5350 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{5047 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
5351 array_len, instrs.len,5048 array_len, instrs.len,
5352 });5049 });
5353 },5050 },
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);5052 else => unreachable,
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);
5500 }5053 }
5501}5054}
55025055
...@@ -5774,8 +5327,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v...@@ -5774,8 +5327,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
5774 const tracy = trace(@src());5327 const tracy = trace(@src());
5775 defer tracy.end();5328 defer tracy.end();
57765329
5777 const pt = sema.pt;
5778 const zcu = pt.zcu;
5779 const zir_tags = sema.code.instructions.items(.tag);5330 const zir_tags = sema.code.instructions.items(.tag);
5780 const zir_datas = sema.code.instructions.items(.data);5331 const zir_datas = sema.code.instructions.items(.data);
5781 const inst_data = zir_datas[@intFromEnum(inst)].pl_node;5332 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...@@ -5789,16 +5340,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
5789 else5340 else
5790 false;5341 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
5802 const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });5343 const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
5803 const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });5344 const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });
5804 const air_tag: Air.Inst.Tag = if (is_ret)5345 const air_tag: Air.Inst.Tag = if (is_ret)
...@@ -28015,15 +27556,24 @@ fn unionFieldPtr(...@@ -28015,15 +27556,24 @@ fn unionFieldPtr(
28015 return Air.internedToRef(field_ptr_val.toIntern());27556 return Air.internedToRef(field_ptr_val.toIntern());
28016 }27557 }
2801727558
28018 if (!initializing and union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and27559 // If the union has a tag, we must either set or or safety check it depending on `initializing`.
28019 union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1)27560 tag: {
28020 {27561 if (union_ty.containerLayout(zcu) != .auto) break :tag;
28021 const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);27562 const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty);
28022 const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());27563 if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag;
28023 // TODO would it be better if get_union_tag supported pointers to unions?27564 // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
28024 const union_val = try block.addTyOp(.load, union_ty, union_ptr);27565 // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
28025 const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_val);27566 const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
28026 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag);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 }
28027 }27577 }
28028 if (field_ty.zigTypeTag(zcu) == .noreturn) {27578 if (field_ty.zigTypeTag(zcu) == .noreturn) {
28029 _ = try block.addNoOp(.unreach);27579 _ = try block.addNoOp(.unreach);
test/behavior/array.zig+13-1
...@@ -540,7 +540,6 @@ test "sentinel element count towards the ABI size calculation" {...@@ -540,7 +540,6 @@ test "sentinel element count towards the ABI size calculation" {
540}540}
541541
542test "zero-sized array with recursive type definition" {542test "zero-sized array with recursive type definition" {
543 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
544 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO543 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
545 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;544 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"...@@ -1098,3 +1097,16 @@ test "initialize pointer to anyopaque with reference to empty array initializer"
1098 // We can't check the value, but it's zero-bit, so the type matching is good enough.1097 // We can't check the value, but it's zero-bit, so the type matching is good enough.
1099 comptime assert(@TypeOf(loaded) == @TypeOf(.{}));1098 comptime assert(@TypeOf(loaded) == @TypeOf(.{}));
1100}1099}
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" {...@@ -217,6 +217,7 @@ test "load non byte-sized value in struct" {
217test "load non byte-sized value in union" {217test "load non byte-sized value in union" {
218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
220 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
220 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;221 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
221 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;222 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;...@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4test "@fieldParentPtr struct" {4test "@fieldParentPtr struct" {
5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;5 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
87
...@@ -587,10 +586,12 @@ test "@fieldParentPtr extern struct last zero-bit field" {...@@ -587,10 +586,12 @@ test "@fieldParentPtr extern struct last zero-bit field" {
587}586}
588587
589test "@fieldParentPtr unaligned packed struct" {588test "@fieldParentPtr unaligned packed struct" {
589 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
590 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;590 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
591 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;591 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
592 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;592 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
593 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;593 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
594 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
594595
595 const C = packed struct {596 const C = packed struct {
596 a: bool = true,597 a: bool = true,
...@@ -725,10 +726,12 @@ test "@fieldParentPtr unaligned packed struct" {...@@ -725,10 +726,12 @@ test "@fieldParentPtr unaligned packed struct" {
725}726}
726727
727test "@fieldParentPtr aligned packed struct" {728test "@fieldParentPtr aligned packed struct" {
729 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
728 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;730 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
729 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;731 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
730 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;732 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
731 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;733 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
734 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
732735
733 const C = packed struct {736 const C = packed struct {
734 a: f32 = 3.14,737 a: f32 = 3.14,
...@@ -866,6 +869,7 @@ test "@fieldParentPtr nested packed struct" {...@@ -866,6 +869,7 @@ test "@fieldParentPtr nested packed struct" {
866 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;869 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
867 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;870 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
868 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;871 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
872 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
869873
870 {874 {
871 const C = packed struct {875 const C = packed struct {
...@@ -1340,7 +1344,6 @@ test "@fieldParentPtr packed struct last zero-bit field" {...@@ -1340,7 +1344,6 @@ test "@fieldParentPtr packed struct last zero-bit field" {
1340}1344}
13411345
1342test "@fieldParentPtr tagged union" {1346test "@fieldParentPtr tagged union" {
1343 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1344 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1347 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1345 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1348 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
13461349
...@@ -1477,7 +1480,6 @@ test "@fieldParentPtr tagged union" {...@@ -1477,7 +1480,6 @@ test "@fieldParentPtr tagged union" {
1477}1480}
14781481
1479test "@fieldParentPtr untagged union" {1482test "@fieldParentPtr untagged union" {
1480 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1481 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1483 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1482 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1484 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
14831485
...@@ -1614,6 +1616,7 @@ test "@fieldParentPtr untagged union" {...@@ -1614,6 +1616,7 @@ test "@fieldParentPtr untagged union" {
1614}1616}
16151617
1616test "@fieldParentPtr extern union" {1618test "@fieldParentPtr extern union" {
1619 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1617 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1620 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1618 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1621 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" {...@@ -1319,6 +1319,7 @@ test "packed struct equality ignores padding bits" {
1319}1319}
13201320
1321test "packed struct with signed field" {1321test "packed struct with signed field" {
1322 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1322 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1323 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
13231324
1324 var s: packed struct {1325 var s: packed struct {