| ... | ... | @@ -836,7 +836,12 @@ pub fn analyzeBody( |
| 836 | 836 | continue; |
| 837 | 837 | }, |
| 838 | 838 | .validate_array_init => { |
| 839 | | try sema.zirValidateArrayInit(block, inst); |
| 839 | try sema.zirValidateArrayInit(block, inst, false); |
| 840 | i += 1; |
| 841 | continue; |
| 842 | }, |
| 843 | .validate_array_init_comptime => { |
| 844 | try sema.zirValidateArrayInit(block, inst, true); |
| 840 | 845 | i += 1; |
| 841 | 846 | continue; |
| 842 | 847 | }, |
| ... | ... | @@ -2815,13 +2820,18 @@ fn validateStructInit( |
| 2815 | 2820 | } |
| 2816 | 2821 | } |
| 2817 | 2822 | |
| 2818 | | fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 2823 | fn zirValidateArrayInit( |
| 2824 | sema: *Sema, |
| 2825 | block: *Block, |
| 2826 | inst: Zir.Inst.Index, |
| 2827 | is_comptime: bool, |
| 2828 | ) CompileError!void { |
| 2819 | 2829 | const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; |
| 2820 | 2830 | const init_src = validate_inst.src(); |
| 2821 | 2831 | const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); |
| 2822 | 2832 | const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; |
| 2823 | | const elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; |
| 2824 | | const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, elem_ptr_data.payload_index).data; |
| 2833 | const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; |
| 2834 | const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; |
| 2825 | 2835 | const array_ptr = sema.resolveInst(elem_ptr_extra.ptr); |
| 2826 | 2836 | const array_ty = sema.typeOf(array_ptr).childType(); |
| 2827 | 2837 | const array_len = array_ty.arrayLen(); |
| ... | ... | @@ -2831,6 +2841,82 @@ fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil |
| 2831 | 2841 | array_len, instrs.len, |
| 2832 | 2842 | }); |
| 2833 | 2843 | } |
| 2844 | |
| 2845 | if (is_comptime or block.is_comptime) { |
| 2846 | // In this case the comptime machinery will have evaluated the store instructions |
| 2847 | // at comptime and we have nothing to do here. |
| 2848 | return; |
| 2849 | } |
| 2850 | |
| 2851 | var array_is_comptime = true; |
| 2852 | var first_block_index: usize = std.math.maxInt(u32); |
| 2853 | |
| 2854 | // Collect the comptime element values in case the array literal ends up |
| 2855 | // being comptime-known. |
| 2856 | const element_vals = try sema.arena.alloc(Value, instrs.len); |
| 2857 | const opt_opv = try sema.typeHasOnePossibleValue(block, init_src, array_ty); |
| 2858 | const air_tags = sema.air_instructions.items(.tag); |
| 2859 | const air_datas = sema.air_instructions.items(.data); |
| 2860 | |
| 2861 | for (instrs) |elem_ptr, i| { |
| 2862 | const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node; |
| 2863 | const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node }; |
| 2864 | |
| 2865 | // Determine whether the value stored to this pointer is comptime-known. |
| 2866 | |
| 2867 | if (opt_opv) |opv| { |
| 2868 | element_vals[i] = opv; |
| 2869 | continue; |
| 2870 | } |
| 2871 | |
| 2872 | const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?; |
| 2873 | const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?; |
| 2874 | // Find the block index of the elem_ptr so that we can look at the next |
| 2875 | // instruction after it within the same block. |
| 2876 | // Possible performance enhancement: save the `block_index` between iterations |
| 2877 | // of the for loop. |
| 2878 | const next_air_inst = inst: { |
| 2879 | var block_index = block.instructions.items.len - 1; |
| 2880 | while (block.instructions.items[block_index] != elem_ptr_air_inst) { |
| 2881 | block_index -= 1; |
| 2882 | } |
| 2883 | first_block_index = @minimum(first_block_index, block_index); |
| 2884 | break :inst block.instructions.items[block_index + 1]; |
| 2885 | }; |
| 2886 | |
| 2887 | // If the next instructon is a store with a comptime operand, this element |
| 2888 | // is comptime. |
| 2889 | switch (air_tags[next_air_inst]) { |
| 2890 | .store => { |
| 2891 | const bin_op = air_datas[next_air_inst].bin_op; |
| 2892 | if (bin_op.lhs != elem_ptr_air_ref) { |
| 2893 | array_is_comptime = false; |
| 2894 | continue; |
| 2895 | } |
| 2896 | if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| { |
| 2897 | element_vals[i] = val; |
| 2898 | } else { |
| 2899 | array_is_comptime = false; |
| 2900 | } |
| 2901 | continue; |
| 2902 | }, |
| 2903 | else => { |
| 2904 | array_is_comptime = false; |
| 2905 | continue; |
| 2906 | }, |
| 2907 | } |
| 2908 | } |
| 2909 | |
| 2910 | if (array_is_comptime) { |
| 2911 | // Our task is to delete all the `elem_ptr` and `store` instructions, and insert |
| 2912 | // instead a single `store` to the array_ptr with a comptime struct value. |
| 2913 | |
| 2914 | block.instructions.shrinkRetainingCapacity(first_block_index); |
| 2915 | |
| 2916 | const array_val = try Value.Tag.array.create(sema.arena, element_vals); |
| 2917 | const array_init = try sema.addConstant(array_ty, array_val); |
| 2918 | try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); |
| 2919 | } |
| 2834 | 2920 | } |
| 2835 | 2921 | |
| 2836 | 2922 | fn failWithBadMemberAccess( |
| ... | ... | @@ -14085,88 +14171,112 @@ fn beginComptimePtrMutation( |
| 14085 | 14171 | .elem_ptr => { |
| 14086 | 14172 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; |
| 14087 | 14173 | var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr); |
| 14088 | | const elem_ty = parent.ty.childType(); |
| 14089 | | switch (parent.val.tag()) { |
| 14090 | | .undef => { |
| 14091 | | // An array has been initialized to undefined at comptime and now we |
| 14092 | | // are for the first time setting an element. We must change the representation |
| 14093 | | // of the array from `undef` to `array`. |
| 14094 | | const arena = parent.beginArena(sema.gpa); |
| 14095 | | defer parent.finishArena(); |
| 14174 | switch (parent.ty.zigTypeTag()) { |
| 14175 | .Array, .Vector => { |
| 14176 | const check_len = parent.ty.arrayLenIncludingSentinel(); |
| 14177 | if (elem_ptr.index >= check_len) { |
| 14178 | // TODO have the parent include the decl so we can say "declared here" |
| 14179 | return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ |
| 14180 | elem_ptr.index, check_len, |
| 14181 | }); |
| 14182 | } |
| 14183 | const elem_ty = parent.ty.childType(); |
| 14184 | switch (parent.val.tag()) { |
| 14185 | .undef => { |
| 14186 | // An array has been initialized to undefined at comptime and now we |
| 14187 | // are for the first time setting an element. We must change the representation |
| 14188 | // of the array from `undef` to `array`. |
| 14189 | const arena = parent.beginArena(sema.gpa); |
| 14190 | defer parent.finishArena(); |
| 14191 | |
| 14192 | const array_len_including_sentinel = |
| 14193 | try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); |
| 14194 | const elems = try arena.alloc(Value, array_len_including_sentinel); |
| 14195 | mem.set(Value, elems, Value.undef); |
| 14196 | |
| 14197 | parent.val.* = try Value.Tag.array.create(arena, elems); |
| 14096 | 14198 | |
| 14097 | | const array_len_including_sentinel = |
| 14098 | | try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); |
| 14099 | | const elems = try arena.alloc(Value, array_len_including_sentinel); |
| 14100 | | mem.set(Value, elems, Value.undef); |
| 14199 | return ComptimePtrMutationKit{ |
| 14200 | .decl_ref_mut = parent.decl_ref_mut, |
| 14201 | .val = &elems[elem_ptr.index], |
| 14202 | .ty = elem_ty, |
| 14203 | }; |
| 14204 | }, |
| 14205 | .bytes => { |
| 14206 | // An array is memory-optimized to store a slice of bytes, but we are about |
| 14207 | // to modify an individual field and the representation has to change. |
| 14208 | // If we wanted to avoid this, there would need to be special detection |
| 14209 | // elsewhere to identify when writing a value to an array element that is stored |
| 14210 | // using the `bytes` tag, and handle it without making a call to this function. |
| 14211 | const arena = parent.beginArena(sema.gpa); |
| 14212 | defer parent.finishArena(); |
| 14213 | |
| 14214 | const bytes = parent.val.castTag(.bytes).?.data; |
| 14215 | const dest_len = parent.ty.arrayLenIncludingSentinel(); |
| 14216 | // bytes.len may be one greater than dest_len because of the case when |
| 14217 | // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. |
| 14218 | assert(bytes.len >= dest_len); |
| 14219 | const elems = try arena.alloc(Value, @intCast(usize, dest_len)); |
| 14220 | for (elems) |*elem, i| { |
| 14221 | elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); |
| 14222 | } |
| 14101 | 14223 | |
| 14102 | | parent.val.* = try Value.Tag.array.create(arena, elems); |
| 14224 | parent.val.* = try Value.Tag.array.create(arena, elems); |
| 14103 | 14225 | |
| 14104 | | return ComptimePtrMutationKit{ |
| 14105 | | .decl_ref_mut = parent.decl_ref_mut, |
| 14106 | | .val = &elems[elem_ptr.index], |
| 14107 | | .ty = elem_ty, |
| 14108 | | }; |
| 14109 | | }, |
| 14110 | | .bytes => { |
| 14111 | | // An array is memory-optimized to store a slice of bytes, but we are about |
| 14112 | | // to modify an individual field and the representation has to change. |
| 14113 | | // If we wanted to avoid this, there would need to be special detection |
| 14114 | | // elsewhere to identify when writing a value to an array element that is stored |
| 14115 | | // using the `bytes` tag, and handle it without making a call to this function. |
| 14116 | | const arena = parent.beginArena(sema.gpa); |
| 14117 | | defer parent.finishArena(); |
| 14226 | return ComptimePtrMutationKit{ |
| 14227 | .decl_ref_mut = parent.decl_ref_mut, |
| 14228 | .val = &elems[elem_ptr.index], |
| 14229 | .ty = elem_ty, |
| 14230 | }; |
| 14231 | }, |
| 14232 | .repeated => { |
| 14233 | // An array is memory-optimized to store only a single element value, and |
| 14234 | // that value is understood to be the same for the entire length of the array. |
| 14235 | // However, now we want to modify an individual field and so the |
| 14236 | // representation has to change. If we wanted to avoid this, there would |
| 14237 | // need to be special detection elsewhere to identify when writing a value to an |
| 14238 | // array element that is stored using the `repeated` tag, and handle it |
| 14239 | // without making a call to this function. |
| 14240 | const arena = parent.beginArena(sema.gpa); |
| 14241 | defer parent.finishArena(); |
| 14242 | |
| 14243 | const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena); |
| 14244 | const array_len_including_sentinel = |
| 14245 | try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); |
| 14246 | const elems = try arena.alloc(Value, array_len_including_sentinel); |
| 14247 | mem.set(Value, elems, repeated_val); |
| 14248 | |
| 14249 | parent.val.* = try Value.Tag.array.create(arena, elems); |
| 14118 | 14250 | |
| 14119 | | const bytes = parent.val.castTag(.bytes).?.data; |
| 14120 | | const dest_len = parent.ty.arrayLenIncludingSentinel(); |
| 14121 | | // bytes.len may be one greater than dest_len because of the case when |
| 14122 | | // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. |
| 14123 | | assert(bytes.len >= dest_len); |
| 14124 | | const elems = try arena.alloc(Value, @intCast(usize, dest_len)); |
| 14125 | | for (elems) |*elem, i| { |
| 14126 | | elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); |
| 14127 | | } |
| 14251 | return ComptimePtrMutationKit{ |
| 14252 | .decl_ref_mut = parent.decl_ref_mut, |
| 14253 | .val = &elems[elem_ptr.index], |
| 14254 | .ty = elem_ty, |
| 14255 | }; |
| 14256 | }, |
| 14128 | 14257 | |
| 14129 | | parent.val.* = try Value.Tag.array.create(arena, elems); |
| 14258 | .array => return ComptimePtrMutationKit{ |
| 14259 | .decl_ref_mut = parent.decl_ref_mut, |
| 14260 | .val = &parent.val.castTag(.array).?.data[elem_ptr.index], |
| 14261 | .ty = elem_ty, |
| 14262 | }, |
| 14130 | 14263 | |
| 14131 | | return ComptimePtrMutationKit{ |
| 14132 | | .decl_ref_mut = parent.decl_ref_mut, |
| 14133 | | .val = &elems[elem_ptr.index], |
| 14134 | | .ty = elem_ty, |
| 14135 | | }; |
| 14264 | else => unreachable, |
| 14265 | } |
| 14136 | 14266 | }, |
| 14137 | | .repeated => { |
| 14138 | | // An array is memory-optimized to store only a single element value, and |
| 14139 | | // that value is understood to be the same for the entire length of the array. |
| 14140 | | // However, now we want to modify an individual field and so the |
| 14141 | | // representation has to change. If we wanted to avoid this, there would |
| 14142 | | // need to be special detection elsewhere to identify when writing a value to an |
| 14143 | | // array element that is stored using the `repeated` tag, and handle it |
| 14144 | | // without making a call to this function. |
| 14145 | | const arena = parent.beginArena(sema.gpa); |
| 14146 | | defer parent.finishArena(); |
| 14147 | | |
| 14148 | | const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena); |
| 14149 | | const array_len_including_sentinel = |
| 14150 | | try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); |
| 14151 | | const elems = try arena.alloc(Value, array_len_including_sentinel); |
| 14152 | | mem.set(Value, elems, repeated_val); |
| 14153 | | |
| 14154 | | parent.val.* = try Value.Tag.array.create(arena, elems); |
| 14155 | | |
| 14267 | else => { |
| 14268 | if (elem_ptr.index != 0) { |
| 14269 | // TODO include a "declared here" note for the decl |
| 14270 | return sema.fail(block, src, "out of bounds comptime store of index {d}", .{ |
| 14271 | elem_ptr.index, |
| 14272 | }); |
| 14273 | } |
| 14156 | 14274 | return ComptimePtrMutationKit{ |
| 14157 | 14275 | .decl_ref_mut = parent.decl_ref_mut, |
| 14158 | | .val = &elems[elem_ptr.index], |
| 14159 | | .ty = elem_ty, |
| 14276 | .val = parent.val, |
| 14277 | .ty = parent.ty, |
| 14160 | 14278 | }; |
| 14161 | 14279 | }, |
| 14162 | | |
| 14163 | | .array => return ComptimePtrMutationKit{ |
| 14164 | | .decl_ref_mut = parent.decl_ref_mut, |
| 14165 | | .val = &parent.val.castTag(.array).?.data[elem_ptr.index], |
| 14166 | | .ty = elem_ty, |
| 14167 | | }, |
| 14168 | | |
| 14169 | | else => unreachable, |
| 14170 | 14280 | } |
| 14171 | 14281 | }, |
| 14172 | 14282 | .field_ptr => { |
| ... | ... | @@ -14296,15 +14406,41 @@ fn beginComptimePtrLoad( |
| 14296 | 14406 | .elem_ptr => { |
| 14297 | 14407 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; |
| 14298 | 14408 | const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr); |
| 14299 | | const elem_ty = parent.ty.childType(); |
| 14300 | | const elem_size = elem_ty.abiSize(target); |
| 14301 | | return ComptimePtrLoadKit{ |
| 14302 | | .root_val = parent.root_val, |
| 14303 | | .val = try parent.val.elemValue(sema.arena, elem_ptr.index), |
| 14304 | | .ty = elem_ty, |
| 14305 | | .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index), |
| 14306 | | .is_mutable = parent.is_mutable, |
| 14307 | | }; |
| 14409 | switch (parent.ty.zigTypeTag()) { |
| 14410 | .Array, .Vector => { |
| 14411 | const check_len = parent.ty.arrayLenIncludingSentinel(); |
| 14412 | if (elem_ptr.index >= check_len) { |
| 14413 | // TODO have the parent include the decl so we can say "declared here" |
| 14414 | return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{ |
| 14415 | elem_ptr.index, check_len, |
| 14416 | }); |
| 14417 | } |
| 14418 | const elem_ty = parent.ty.childType(); |
| 14419 | const elem_size = elem_ty.abiSize(target); |
| 14420 | return ComptimePtrLoadKit{ |
| 14421 | .root_val = parent.root_val, |
| 14422 | .val = try parent.val.elemValue(sema.arena, elem_ptr.index), |
| 14423 | .ty = elem_ty, |
| 14424 | .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index), |
| 14425 | .is_mutable = parent.is_mutable, |
| 14426 | }; |
| 14427 | }, |
| 14428 | else => { |
| 14429 | if (elem_ptr.index != 0) { |
| 14430 | // TODO have the parent include the decl so we can say "declared here" |
| 14431 | return sema.fail(block, src, "out of bounds comptime load of index {d}", .{ |
| 14432 | elem_ptr.index, |
| 14433 | }); |
| 14434 | } |
| 14435 | return ComptimePtrLoadKit{ |
| 14436 | .root_val = parent.root_val, |
| 14437 | .val = parent.val, |
| 14438 | .ty = parent.ty, |
| 14439 | .byte_offset = parent.byte_offset, |
| 14440 | .is_mutable = parent.is_mutable, |
| 14441 | }; |
| 14442 | }, |
| 14443 | } |
| 14308 | 14444 | }, |
| 14309 | 14445 | .field_ptr => { |
| 14310 | 14446 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; |