| ... | @@ -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 | } |
| 4056 | | 4056 | |
| 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`, or | 4058 | // 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. |
| 4060 | | 4060 | |
| 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 above | 4064 | .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 to | 4113 | // 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. |
| 4101 | | 4115 | |
| 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 to | 4117 | // 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 = .{ |
| 4106 | | 4120 | .ty = .fromIntern(alloc_ty.toIntern()), |
| 4107 | // The specifics of this instruction aren't really important: we just want | 4121 | .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 } } }; | | |
| 4110 | | 4123 | |
| 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); |
| 4882 | | 4875 | |
| 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 | } |
| 5009 | | 4886 | |
| 5010 | fn validateStructInit( | 4887 | fn 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; |
| 5021 | | 4899 | |
| 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); |
| 5029 | | 4904 | |
| 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 | else | 4917 | 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 | } |
| 5050 | | 4922 | |
| 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. |
| 5053 | | 4926 | |
| 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() and | 4928 | // 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 implicit | 4932 | // 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 | // |
| 5065 | | 4938 | // * 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); | | |
| 5116 | | 4940 | |
| 5117 | // We collect the comptime field values in case the struct initialization | 4941 | 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)); | | |
| 5120 | | 4943 | |
| 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 | } | | |
| 5196 | | 4947 | |
| | 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; | | |
| 5281 | | 4969 | |
| 5282 | const field_src = init_src; // TODO better source location | 4970 | 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 | else | 4973 | 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 | } |
| 5292 | | 4985 | |
| ... | @@ -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); |
| 5309 | | 5002 | |
| 5310 | // Collect the comptime element values in case the array literal ends up | 5003 | // 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 | ); | | |
| 5316 | | 5008 | |
| 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); |
| 5321 | | 5013 | |
| ... | @@ -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 | } |
| 5338 | | 5028 | |
| 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; | | |
| 5382 | | 5051 | |
| 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 | } |
| 5502 | | 5055 | |
| ... | @@ -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(); |
| 5776 | | 5329 | |
| 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 | else | 5340 | else |
| 5790 | false; | 5341 | false; |
| 5791 | | 5342 | |
| 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 | } |
| 28017 | | 27558 | |
| 28018 | if (!initializing and union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and | 27559 | // 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); |