╭ comptime_ptr_access.zig ──────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 pub const ComptimeLoadResult = union(enum) { 2 success: MutableValue, 3 4 runtime_load, 5 undef, 6 err_payload: InternPool.NullTerminatedString, 7 null_payload, 8 inactive_union_field, 9 needed_well_defined: Type, 10 out_of_bounds: Type, 11 exceeds_host_size, 12 }; 13 14 pub fn loadComptimePtr(sema: *Sema, block: *Block, src: LazySrcLoc, ptr: Value) !ComptimeLoadResult { 15 const pt = sema.pt; 16 const zcu = pt.zcu; 17 18 const ptr_info = ptr.typeOf(pt.zcu).ptrInfo(pt.zcu); 19 const elem_ty: Type = .fromInterned(ptr_info.child); 20 const host_size = ptr_info.packed_offset.host_size; 21 22 if (host_size == 0) { 23 return loadComptimePtrInner(sema, block, src, ptr, elem_ty, 0); 24 } 25 26 assert(elem_ty.hasBitRepresentation(zcu)); 27 if (ptr_info.flags.vector_index == .none) { 28 if (ptr_info.packed_offset.bit_offset + elem_ty.bitSize(zcu) > host_size * 8) { 29 return .exceeds_host_size; 30 } 31 const load_ty: Type = try pt.intType(.unsigned, host_size * 8); 32 const backing_int_mv = switch (try loadComptimePtrInner(sema, block, src, ptr, load_ty, 0)) { 33 else => |result| return result, 34 .success => |mv| mv, 35 }; 36 const backing_int_val = try backing_int_mv.intern(pt, sema.arena); 37 const buf = try sema.arena.alloc(u8, host_size); 38 @memset(buf, 0); 39 backing_int_val.writeToPackedMemory(zcu, buf, 0); 40 const result_val: Value = try .readFromPackedMemory(elem_ty, pt, buf, ptr_info.packed_offset.bit_offset); 41 return .{ .success = .{ .interned = result_val.toIntern() } }; 42 } 43 if (@backingInt(ptr_info.flags.vector_index) >= host_size) { 44 return .exceeds_host_size; 45 } 46 const load_ty: Type = try pt.vectorType(.{ 47 .len = host_size, 48 .child = elem_ty.toIntern(), 49 }); 50 const vector_mv = switch (try loadComptimePtrInner(sema, block, src, ptr, load_ty, 0)) { 51 else => |result| return result, 52 .success => |mv| mv, 53 }; 54 const vector_val = try vector_mv.intern(pt, sema.arena); 55 const result_val = try vector_val.elemValue(pt, @backingInt(ptr_info.flags.vector_index)); 56 return .{ .success = .{ .interned = result_val.toIntern() } }; 57 } 58 59 pub const ComptimeStoreResult = union(enum) { 60 success, 61 62 runtime_store, 63 comptime_field_mismatch: Value, 64 undef, 65 err_payload: InternPool.NullTerminatedString, 66 null_payload, 67 inactive_union_field, 68 needed_well_defined: Type, 69 out_of_bounds: Type, 70 exceeds_host_size, 71 }; 72 73 /// Perform a comptime load of value `store_val` to a pointer. 74 /// 75 /// Asserts that the type of `store_val` equals the element type of the pointer type. 76 pub fn storeComptimePtr( 77 sema: *Sema, 78 block: *Block, 79 src: LazySrcLoc, 80 ptr: Value, 81 store_val: Value, 82 ) !ComptimeStoreResult { 83 const pt = sema.pt; 84 const zcu = pt.zcu; 85 86 const ptr_info = ptr.typeOf(pt.zcu).ptrInfo(pt.zcu); 87 const elem_ty: Type = .fromInterned(ptr_info.child); 88 const host_size = ptr_info.packed_offset.host_size; 89 assert(store_val.typeOf(zcu).toIntern() == elem_ty.toIntern()); 90 91 if (host_size == 0) { 92 return storeComptimePtrInner(sema, block, src, ptr, store_val); 93 } 94 95 assert(elem_ty.hasBitRepresentation(zcu)); 96 if (ptr_info.flags.vector_index == .none) { 97 if (ptr_info.packed_offset.bit_offset + elem_ty.bitSize(zcu) > host_size * 8) { 98 return .exceeds_host_size; 99 } 100 const backing_ty: Type = try pt.intType(.unsigned, host_size * 8); 101 const backing_int_mv = switch (try loadComptimePtrInner(sema, block, src, ptr, backing_ty, 0)) { 102 .success => |mv| mv, 103 .runtime_load => return .runtime_store, 104 inline else => |payload, tag| return @unionInit(ComptimeStoreResult, @tagName(tag), payload), 105 }; 106 const old_backing_int_val = try backing_int_mv.intern(pt, sema.arena); 107 const buf = try sema.arena.alloc(u8, host_size); 108 @memset(buf, 0); 109 old_backing_int_val.writeToPackedMemory(zcu, buf, 0); 110 // Write the new element... 111 store_val.writeToPackedMemory(zcu, buf, ptr_info.packed_offset.bit_offset); 112 // ...then read the resulting backing integer value... 113 const new_backing_int_val: Value = try .readFromPackedMemory(backing_ty, pt, buf, 0); 114 // ...and store that back into memory 115 return storeComptimePtrInner(sema, block, src, ptr, new_backing_int_val); 116 } 117 118 if (@backingInt(ptr_info.flags.vector_index) >= host_size) { 119 return .exceeds_host_size; 120 } 121 const vec_ty: Type = try pt.vectorType(.{ 122 .len = host_size, 123 .child = elem_ty.toIntern(), 124 }); 125 const vector_mv = switch (try loadComptimePtrInner(sema, block, src, ptr, vec_ty, 0)) { 126 .success => |mv| mv, 127 .runtime_load => return .runtime_store, 128 inline else => |payload, tag| return @unionInit(ComptimeStoreResult, @tagName(tag), payload), 129 }; 130 const old_vector_val = try vector_mv.intern(pt, sema.arena); 131 const elems_buf = try sema.arena.alloc(InternPool.Index, host_size); 132 for (elems_buf, 0..) |*elem, elem_index| { 133 const elem_val = try old_vector_val.elemValue(pt, elem_index); 134 elem.* = elem_val.toIntern(); 135 } 136 elems_buf[@backingInt(ptr_info.flags.vector_index)] = store_val.toIntern(); 137 const new_vector_val = try pt.aggregateValue(vec_ty, elems_buf); 138 return storeComptimePtrInner(sema, block, src, ptr, new_vector_val); 139 } 140 141 /// Like `storeComptimePtr`, except ignores the type of `ptr`, instead treating it as a single-item 142 /// pointer to `store_val.typeOf(zcu)`. 143 fn storeComptimePtrInner( 144 sema: *Sema, 145 block: *Block, 146 src: LazySrcLoc, 147 ptr: Value, 148 store_val: Value, 149 ) !ComptimeStoreResult { 150 const pt = sema.pt; 151 const zcu = pt.zcu; 152 const store_ty = store_val.typeOf(zcu); 153 154 if (store_ty.classify(zcu) == .one_possible_value) { 155 // zero-bit store; nothing to do 156 return .success; 157 } 158 159 const strat = try prepareComptimePtrStore(sema, block, src, ptr, store_ty, 0); 160 161 // Propagate errors and handle comptime fields. 162 switch (strat) { 163 .comptime_field => { 164 // To "store" to a comptime field, just perform a load of the field 165 // and see if the store value matches. 166 const expected_mv = switch (try loadComptimePtr(sema, block, src, ptr)) { 167 .success => |mv| mv, 168 .runtime_load => unreachable, // this is a comptime field 169 .exceeds_host_size => unreachable, // checked above 170 .undef => return .undef, 171 .err_payload => |err| return .{ .err_payload = err }, 172 .null_payload => return .null_payload, 173 .inactive_union_field => return .inactive_union_field, 174 .needed_well_defined => |ty| return .{ .needed_well_defined = ty }, 175 .out_of_bounds => |ty| return .{ .out_of_bounds = ty }, 176 }; 177 const expected = try expected_mv.intern(pt, sema.arena); 178 if (store_val.toIntern() != expected.toIntern()) { 179 return .{ .comptime_field_mismatch = expected }; 180 } 181 return .success; 182 }, 183 .runtime_store => return .runtime_store, 184 .undef => return .undef, 185 .err_payload => |err| return .{ .err_payload = err }, 186 .null_payload => return .null_payload, 187 .inactive_union_field => return .inactive_union_field, 188 .needed_well_defined => |ty| return .{ .needed_well_defined = ty }, 189 .out_of_bounds => |ty| return .{ .out_of_bounds = ty }, 190 191 .direct => |direct| { 192 try checkComptimeVarStore(sema, block, src, direct.alloc); 193 const want_ty = direct.val.typeOf(zcu); 194 const coerced_store_val = try pt.getCoerced(store_val, want_ty); 195 direct.val.* = .{ .interned = coerced_store_val.toIntern() }; 196 return .success; 197 }, 198 199 .index => |index| { 200 try checkComptimeVarStore(sema, block, src, index.alloc); 201 const want_ty = index.val.typeOf(zcu).childType(zcu); 202 const coerced_store_val = try pt.getCoerced(store_val, want_ty); 203 try index.val.setElem(pt, sema.arena, @intCast(index.elem_index), .{ .interned = coerced_store_val.toIntern() }); 204 return .success; 205 }, 206 207 .flat_index => |flat| { 208 try checkComptimeVarStore(sema, block, src, flat.alloc); 209 const store_elems = store_val.typeOf(zcu).arrayBase(zcu)[1]; 210 const flat_elems = try sema.arena.alloc(InternPool.Index, @intCast(store_elems)); 211 { 212 var next_idx: u64 = 0; 213 var skip: u64 = 0; 214 try flattenArray(sema, .{ .interned = store_val.toIntern() }, &skip, &next_idx, flat_elems); 215 } 216 for (flat_elems, 0..) |elem, idx| { 217 // TODO: recursiveIndex in a loop does a lot of redundant work! 218 // Better would be to gather all the store targets into an array. 219 var index: u64 = flat.flat_elem_index + idx; 220 const val_ptr, const final_idx = (try recursiveIndex(sema, flat.val, &index)).?; 221 try val_ptr.setElem(pt, sema.arena, @intCast(final_idx), .{ .interned = elem }); 222 } 223 return .success; 224 }, 225 226 .reinterpret => |reinterpret| { 227 try checkComptimeVarStore(sema, block, src, reinterpret.alloc); 228 if (!reinterpret.val.typeOf(zcu).hasWellDefinedLayout(zcu)) { 229 return .{ .needed_well_defined = reinterpret.val.typeOf(zcu) }; 230 } 231 if (!store_ty.hasWellDefinedLayout(zcu)) { 232 return .{ .needed_well_defined = store_ty }; 233 } 234 const old_val = try reinterpret.val.intern(pt, sema.arena); 235 const new_val = try sema.spliceMemory( 236 old_val, 237 store_val, 238 reinterpret.byte_offset, 239 ) orelse return .runtime_store; 240 reinterpret.val.* = .{ .interned = new_val.toIntern() }; 241 return .success; 242 }, 243 } 244 } 245 246 /// Perform a comptime load of type `load_ty` from a pointer. 247 /// The pointer's type is ignored. 248 fn loadComptimePtrInner( 249 sema: *Sema, 250 block: *Block, 251 src: LazySrcLoc, 252 ptr_val: Value, 253 load_ty: Type, 254 /// If `load_ty` is an array, this is the number of array elements to skip 255 /// before `load_ty`. Otherwise, it is ignored and may be `undefined`. 256 array_offset: u64, 257 ) !ComptimeLoadResult { 258 const pt = sema.pt; 259 const zcu = pt.zcu; 260 const ip = &zcu.intern_pool; 261 262 const ptr = switch (ip.indexToKey(ptr_val.toIntern())) { 263 .undef => return .undef, 264 .ptr => |ptr| ptr, 265 else => unreachable, 266 }; 267 268 const base_val: MutableValue = switch (ptr.base_addr) { 269 .nav => |nav_id| val: { 270 try sema.ensureNavResolved(block, src, nav_id, .fully); 271 const nav = ip.getNav(nav_id); 272 if (!nav.resolved.?.@"const") return .runtime_load; 273 // We let `.@"extern"` through here if it's a fn. This allows aliasing `extern fn`s. 274 if (ip.indexToKey(nav.resolved.?.value) == .@"extern" and 275 Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu) != .@"fn") 276 { 277 return .runtime_load; 278 } 279 break :val .{ .interned = nav.resolved.?.value }; 280 }, 281 .comptime_alloc => |alloc_index| sema.getComptimeAlloc(alloc_index).val, 282 .uav => |uav| .{ .interned = uav.val }, 283 .comptime_field => |val| .{ .interned = val }, 284 .int => return .runtime_load, 285 .eu_payload => |base_ptr_ip| val: { 286 const base_ptr = Value.fromInterned(base_ptr_ip); 287 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 288 switch (try loadComptimePtrInner(sema, block, src, base_ptr, base_ty, undefined)) { 289 .success => |eu_val| switch (eu_val.unpackErrorUnion(zcu)) { 290 .undef => return .undef, 291 .err => |err| return .{ .err_payload = err }, 292 .payload => |payload| break :val payload, 293 }, 294 else => |err| return err, 295 } 296 }, 297 .opt_payload => |base_ptr_ip| val: { 298 const base_ptr = Value.fromInterned(base_ptr_ip); 299 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 300 switch (try loadComptimePtrInner(sema, block, src, base_ptr, base_ty, undefined)) { 301 .success => |eu_val| switch (eu_val.unpackOptional(zcu)) { 302 .undef => return .undef, 303 .null => return .null_payload, 304 .payload => |payload| break :val payload, 305 }, 306 else => |err| return err, 307 } 308 }, 309 .arr_elem => |base_index| val: { 310 const base_ptr = Value.fromInterned(base_index.base); 311 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 312 313 // We have a comptime-only array. This case is a little nasty. 314 // To avoid loading too much data, we want to figure out how many elements we need. 315 // If `load_ty` and the array share a base type, we'll load the correct number of elements. 316 // Otherwise, we'll be reinterpreting (which we can't do, since it's comptime-only); just 317 // load a single element and let the logic below emit its error. 318 319 const load_one_ty, const load_count = load_ty.arrayBase(zcu); 320 const count = if (load_one_ty.toIntern() == base_ty.toIntern()) load_count else 1; 321 322 const want_ty = try sema.pt.arrayType(.{ 323 .len = count, 324 .child = base_ty.toIntern(), 325 }); 326 327 switch (try loadComptimePtrInner(sema, block, src, base_ptr, want_ty, base_index.index)) { 328 .success => |arr_val| break :val arr_val, 329 else => |err| return err, 330 } 331 }, 332 .field => |base_index| val: { 333 const base_ptr = Value.fromInterned(base_index.base); 334 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 335 336 // Field of a slice, or of an auto-layout struct or union. 337 const agg_val = switch (try loadComptimePtrInner(sema, block, src, base_ptr, base_ty, undefined)) { 338 .success => |val| val, 339 else => |err| return err, 340 }; 341 342 const agg_ty = agg_val.typeOf(zcu); 343 switch (agg_ty.zigTypeTag(zcu)) { 344 .@"struct", .pointer => break :val try agg_val.getElem(sema.pt, @intCast(base_index.index)), 345 .@"union" => { 346 const tag_val: Value, const payload_mv: MutableValue = switch (agg_val) { 347 .un => |un| .{ Value.fromInterned(un.tag), un.payload.* }, 348 .interned => |ip_index| switch (ip.indexToKey(ip_index)) { 349 .undef => return .undef, 350 .un => |un| .{ Value.fromInterned(un.tag), .{ .interned = un.val } }, 351 else => unreachable, 352 }, 353 else => unreachable, 354 }; 355 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu); 356 if (tag_ty.enumTagFieldIndex(tag_val, zcu).? != base_index.index) { 357 return .inactive_union_field; 358 } 359 break :val payload_mv; 360 }, 361 else => unreachable, 362 } 363 364 break :val try agg_val.getElem(zcu, base_index.index); 365 }, 366 }; 367 368 if (ptr.byte_offset == 0) { 369 if (load_ty.zigTypeTag(zcu) != .array or array_offset == 0) { 370 if (.ok == try sema.coerceInMemoryAllowed( 371 block, 372 load_ty, 373 base_val.typeOf(zcu), 374 false, 375 zcu.getTarget(), 376 src, 377 src, 378 null, 379 )) { 380 // We already have a value which is IMC to the desired type. 381 return .{ .success = base_val }; 382 } 383 } 384 } 385 386 restructure_array: { 387 // We might also be changing the length of an array, or restructuring it. 388 // e.g. [1][2][3]T -> [3][2]T. 389 // This case is important because it's permitted for types with ill-defined layouts. 390 391 const load_one_ty, const load_count = load_ty.arrayBase(zcu); 392 393 const extra_base_index: u64 = if (ptr.byte_offset == 0) 0 else idx: { 394 if (load_one_ty.comptimeOnly(zcu)) break :restructure_array; 395 const elem_len = load_one_ty.abiSize(zcu); 396 if (ptr.byte_offset % elem_len != 0) break :restructure_array; 397 break :idx @divExact(ptr.byte_offset, elem_len); 398 }; 399 400 const val_one_ty, const val_count = base_val.typeOf(zcu).arrayBase(zcu); 401 if (.ok == try sema.coerceInMemoryAllowed( 402 block, 403 load_one_ty, 404 val_one_ty, 405 false, 406 zcu.getTarget(), 407 src, 408 src, 409 null, 410 )) { 411 // Changing the length of an array. 412 const skip_base: u64 = extra_base_index + if (load_ty.zigTypeTag(zcu) == .array) skip: { 413 break :skip load_ty.childType(zcu).arrayBase(zcu)[1] * array_offset; 414 } else 0; 415 if (skip_base + load_count > val_count) return .{ .out_of_bounds = base_val.typeOf(zcu) }; 416 const elems = try sema.arena.alloc(InternPool.Index, @intCast(load_count)); 417 var skip: u64 = skip_base; 418 var next_idx: u64 = 0; 419 try flattenArray(sema, base_val, &skip, &next_idx, elems); 420 next_idx = 0; 421 const val = try unflattenArray(sema, load_ty, elems, &next_idx); 422 return .{ .success = .{ .interned = val.toIntern() } }; 423 } 424 } 425 426 // We need to reinterpret memory, which is only possible if neither the load 427 // type nor the type of the base value are comptime-only. 428 429 if (!load_ty.hasWellDefinedLayout(zcu)) { 430 return .{ .needed_well_defined = load_ty }; 431 } 432 433 if (!base_val.typeOf(zcu).hasWellDefinedLayout(zcu)) { 434 return .{ .needed_well_defined = base_val.typeOf(zcu) }; 435 } 436 437 var cur_val = base_val; 438 var cur_offset = ptr.byte_offset; 439 440 if (load_ty.zigTypeTag(zcu) == .array and array_offset > 0) { 441 cur_offset += load_ty.childType(zcu).abiSize(zcu) * array_offset; 442 } 443 444 const need_bytes = load_ty.abiSize(zcu); 445 446 if (cur_offset + need_bytes > cur_val.typeOf(zcu).abiSize(zcu)) { 447 return .{ .out_of_bounds = cur_val.typeOf(zcu) }; 448 } 449 450 // In the worst case, we can reinterpret the entire value - however, that's 451 // pretty wasteful. If the memory region we're interested in refers to one 452 // field or array element, let's just look at that. 453 while (true) { 454 const cur_ty = cur_val.typeOf(zcu); 455 switch (cur_ty.zigTypeTag(zcu)) { 456 .noreturn, 457 .type, 458 .comptime_int, 459 .comptime_float, 460 .null, 461 .undefined, 462 .enum_literal, 463 .@"opaque", 464 .spirv, 465 .@"fn", 466 .error_union, 467 => unreachable, // ill-defined layout 468 .int, 469 .float, 470 .bool, 471 .void, 472 .pointer, 473 .error_set, 474 .@"anyframe", 475 .frame, 476 .@"enum", 477 .vector, 478 => break, // terminal types (no sub-values) 479 .optional => break, // this can only be a pointer-like optional so is terminal 480 .array => { 481 const elem_ty = cur_ty.childType(zcu); 482 const elem_size = elem_ty.abiSize(zcu); 483 const elem_idx = cur_offset / elem_size; 484 const next_elem_off = elem_size * (elem_idx + 1); 485 if (cur_offset + need_bytes <= next_elem_off) { 486 // We can look at a single array element. 487 cur_val = try cur_val.getElem(sema.pt, @intCast(elem_idx)); 488 cur_offset -= elem_idx * elem_size; 489 } else { 490 break; 491 } 492 }, 493 .@"struct" => switch (cur_ty.containerLayout(zcu)) { 494 .auto => unreachable, // ill-defined layout 495 .@"packed" => break, // let the memory reinterpret logic handle this 496 .@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| { 497 const start_off = cur_ty.structFieldOffset(field_idx, zcu); 498 const end_off = start_off + cur_ty.fieldType(field_idx, zcu).abiSize(zcu); 499 if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) { 500 cur_val = try cur_val.getElem(sema.pt, field_idx); 501 cur_offset -= start_off; 502 break; 503 } 504 } else break, // pointer spans multiple fields 505 }, 506 .@"union" => switch (cur_ty.containerLayout(zcu)) { 507 .auto => unreachable, // ill-defined layout 508 .@"packed" => break, // let the memory reinterpret logic handle this 509 .@"extern" => { 510 // TODO: we have to let the memory reinterpret logic handle this for now. 511 // Otherwise, we might traverse into a union field which doesn't allow pointers. 512 // Figure out a solution! 513 if (true) break; 514 const payload: MutableValue = switch (cur_val) { 515 .un => |un| un.payload.*, 516 .interned => |ip_index| switch (ip.indexToKey(ip_index)) { 517 .un => |un| .{ .interned = un.val }, 518 .undef => return .undef, 519 else => unreachable, 520 }, 521 else => unreachable, 522 }; 523 // The payload always has offset 0. If it's big enough 524 // to represent the whole load type, we can use it. 525 if (payload.typeOf(zcu).abiSize(zcu) >= need_bytes) { 526 cur_val = payload; 527 } else { 528 break; 529 } 530 }, 531 }, 532 } 533 } 534 535 // Fast path: check again if we're now at the type we want to load. 536 // If so, just return the loaded value. 537 if (cur_offset == 0 and cur_val.typeOf(zcu).toIntern() == load_ty.toIntern()) { 538 return .{ .success = cur_val }; 539 } 540 541 // Otherwise, use the memory reinterpretation logic to pull out the bytes we need. 542 const reinterpret_val = try cur_val.intern(pt, sema.arena); 543 const result_val = try sema.castMemory(reinterpret_val, load_ty, cur_offset) orelse return .runtime_load; 544 return .{ .success = .{ .interned = result_val.toIntern() } }; 545 } 546 547 const ComptimeStoreStrategy = union(enum) { 548 /// The store should be performed directly to this value, which `store_ty` 549 /// is in-memory coercible to. 550 direct: struct { 551 alloc: ComptimeAllocIndex, 552 val: *MutableValue, 553 }, 554 /// The store should be performed at the index `elem_index` into `val`, 555 /// which is an array. 556 /// This strategy exists to avoid the need to convert the parent value 557 /// to the `aggregate` representation when `repeated` or `bytes` may 558 /// suffice. 559 index: struct { 560 alloc: ComptimeAllocIndex, 561 val: *MutableValue, 562 elem_index: u64, 563 }, 564 /// The store should be performed on this array value, but it is being 565 /// restructured, e.g. [3][2][1]T -> [2][3]T. 566 /// This includes the case where it is a sub-array, e.g. [3]T -> [2]T. 567 /// This is only returned if `store_ty` is an array type, and its array 568 /// base type is IMC to that of the type of `val`. 569 flat_index: struct { 570 alloc: ComptimeAllocIndex, 571 val: *MutableValue, 572 flat_elem_index: u64, 573 }, 574 /// This value should be reinterpreted using `Sema.spliceMemory` to perform 575 /// store. Only returned if `store_ty` and the type of `val` both have 576 /// well-defined layouts. 577 reinterpret: struct { 578 alloc: ComptimeAllocIndex, 579 val: *MutableValue, 580 byte_offset: u64, 581 }, 582 583 comptime_field, 584 runtime_store, 585 undef, 586 err_payload: InternPool.NullTerminatedString, 587 null_payload, 588 inactive_union_field, 589 needed_well_defined: Type, 590 out_of_bounds: Type, 591 592 fn alloc(strat: ComptimeStoreStrategy) ComptimeAllocIndex { 593 return switch (strat) { 594 inline .direct, .index, .flat_index, .reinterpret => |info| info.alloc, 595 .comptime_field, 596 .runtime_store, 597 .undef, 598 .err_payload, 599 .null_payload, 600 .inactive_union_field, 601 .needed_well_defined, 602 .out_of_bounds, 603 => unreachable, 604 }; 605 } 606 }; 607 608 /// Decide the strategy we will use to perform a comptime store of type `store_ty` to a pointer. 609 /// The pointer's type is ignored. 610 fn prepareComptimePtrStore( 611 sema: *Sema, 612 block: *Block, 613 src: LazySrcLoc, 614 ptr_val: Value, 615 store_ty: Type, 616 /// If `store_ty` is an array, this is the number of array elements to skip 617 /// before `store_ty`. Otherwise, it is ignored and may be `undefined`. 618 array_offset: u64, 619 ) !ComptimeStoreStrategy { 620 const pt = sema.pt; 621 const zcu = pt.zcu; 622 const ip = &zcu.intern_pool; 623 624 const ptr = switch (ip.indexToKey(ptr_val.toIntern())) { 625 .undef => return .undef, 626 .ptr => |ptr| ptr, 627 else => unreachable, 628 }; 629 630 // `base_strat` will not be an error case. 631 const base_strat: ComptimeStoreStrategy = switch (ptr.base_addr) { 632 .nav, .uav, .int => return .runtime_store, 633 .comptime_field => return .comptime_field, 634 .comptime_alloc => |alloc_index| .{ .direct = .{ 635 .alloc = alloc_index, 636 .val = &sema.getComptimeAlloc(alloc_index).val, 637 } }, 638 .eu_payload => |base_ptr_ip| base_val: { 639 const base_ptr = Value.fromInterned(base_ptr_ip); 640 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 641 const eu_val_ptr, const alloc = switch (try prepareComptimePtrStore(sema, block, src, base_ptr, base_ty, undefined)) { 642 .direct => |direct| .{ direct.val, direct.alloc }, 643 .index => |index| .{ 644 try index.val.elem(pt, sema.arena, @intCast(index.elem_index)), 645 index.alloc, 646 }, 647 .flat_index => unreachable, // base_ty is not an array 648 .reinterpret => unreachable, // base_ty has ill-defined layout 649 else => |err| return err, 650 }; 651 try eu_val_ptr.unintern(pt, sema.arena, false, false); 652 switch (eu_val_ptr.*) { 653 .interned => |ip_index| switch (ip.indexToKey(ip_index)) { 654 .undef => return .undef, 655 .error_union => |eu| return .{ .err_payload = eu.val.err_name }, 656 else => unreachable, 657 }, 658 .eu_payload => |data| break :base_val .{ .direct = .{ 659 .val = data.child, 660 .alloc = alloc, 661 } }, 662 else => unreachable, 663 } 664 }, 665 .opt_payload => |base_ptr_ip| base_val: { 666 const base_ptr = Value.fromInterned(base_ptr_ip); 667 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 668 const opt_val_ptr, const alloc = switch (try prepareComptimePtrStore(sema, block, src, base_ptr, base_ty, undefined)) { 669 .direct => |direct| .{ direct.val, direct.alloc }, 670 .index => |index| .{ 671 try index.val.elem(pt, sema.arena, @intCast(index.elem_index)), 672 index.alloc, 673 }, 674 .flat_index => unreachable, // base_ty is not an array 675 .reinterpret => unreachable, // base_ty has ill-defined layout 676 else => |err| return err, 677 }; 678 try opt_val_ptr.unintern(pt, sema.arena, false, false); 679 switch (opt_val_ptr.*) { 680 .interned => |ip_index| switch (ip.indexToKey(ip_index)) { 681 .undef => return .undef, 682 .opt => return .null_payload, 683 else => unreachable, 684 }, 685 .opt_payload => |data| break :base_val .{ .direct = .{ 686 .val = data.child, 687 .alloc = alloc, 688 } }, 689 else => unreachable, 690 } 691 }, 692 .arr_elem => |base_index| base_val: { 693 const base_ptr = Value.fromInterned(base_index.base); 694 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 695 696 // We have a comptime-only array. This case is a little nasty. 697 // To avoid messing with too much data, we want to figure out how many elements we need to store. 698 // If `store_ty` and the array share a base type, we'll store the correct number of elements. 699 // Otherwise, we'll be reinterpreting (which we can't do, since it's comptime-only); just 700 // load a single element and let the logic below emit its error. 701 702 const store_one_ty, const store_count = store_ty.arrayBase(zcu); 703 const count = if (store_one_ty.toIntern() == base_ty.toIntern()) store_count else 1; 704 705 const want_ty = try pt.arrayType(.{ 706 .len = count, 707 .child = base_ty.toIntern(), 708 }); 709 710 const result = try prepareComptimePtrStore(sema, block, src, base_ptr, want_ty, base_index.index); 711 switch (result) { 712 .direct, .index, .flat_index => break :base_val result, 713 .reinterpret => unreachable, // comptime-only array so ill-defined layout 714 else => |err| return err, 715 } 716 }, 717 .field => |base_index| strat: { 718 const base_ptr = Value.fromInterned(base_index.base); 719 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 720 721 // Field of a slice, or of an auto-layout struct or union. 722 const agg_val, const alloc = switch (try prepareComptimePtrStore(sema, block, src, base_ptr, base_ty, undefined)) { 723 .direct => |direct| .{ direct.val, direct.alloc }, 724 .index => |index| .{ 725 try index.val.elem(pt, sema.arena, @intCast(index.elem_index)), 726 index.alloc, 727 }, 728 .flat_index => unreachable, // base_ty is not an array 729 .reinterpret => unreachable, // base_ty has ill-defined layout 730 else => |err| return err, 731 }; 732 733 const agg_ty = agg_val.typeOf(zcu); 734 switch (agg_ty.zigTypeTag(zcu)) { 735 .@"struct", .pointer => break :strat .{ .direct = .{ 736 .val = try agg_val.elem(pt, sema.arena, @intCast(base_index.index)), 737 .alloc = alloc, 738 } }, 739 .@"union" => { 740 if (agg_val.* == .interned and Value.fromInterned(agg_val.interned).isUndef(zcu)) { 741 return .undef; 742 } 743 try agg_val.unintern(pt, sema.arena, false, false); 744 const un = agg_val.un; 745 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu); 746 if (tag_ty.enumTagFieldIndex(Value.fromInterned(un.tag), zcu).? != base_index.index) { 747 return .inactive_union_field; 748 } 749 break :strat .{ .direct = .{ 750 .val = un.payload, 751 .alloc = alloc, 752 } }; 753 }, 754 else => unreachable, 755 } 756 }, 757 }; 758 759 if (ptr.byte_offset == 0) { 760 if (store_ty.zigTypeTag(zcu) != .array or array_offset == 0) direct: { 761 const base_val_ty = switch (base_strat) { 762 .direct => |direct| direct.val.typeOf(zcu), 763 .index => |index| index.val.typeOf(zcu).childType(zcu), 764 .flat_index, .reinterpret => break :direct, 765 else => unreachable, 766 }; 767 if (.ok == try sema.coerceInMemoryAllowed( 768 block, 769 base_val_ty, 770 store_ty, 771 true, 772 zcu.getTarget(), 773 src, 774 src, 775 null, 776 )) { 777 // The base strategy already gets us a value which the desired type is IMC to. 778 return base_strat; 779 } 780 } 781 } 782 783 restructure_array: { 784 // We might also be changing the length of an array, or restructuring it. 785 // e.g. [1][2][3]T -> [3][2]T. 786 // This case is important because it's permitted for types with ill-defined layouts. 787 788 const store_one_ty, const store_count = store_ty.arrayBase(zcu); 789 const extra_base_index: u64 = if (ptr.byte_offset == 0) 0 else idx: { 790 if (store_one_ty.comptimeOnly(zcu)) break :restructure_array; 791 const elem_len = store_one_ty.abiSize(zcu); 792 if (ptr.byte_offset % elem_len != 0) break :restructure_array; 793 break :idx @divExact(ptr.byte_offset, elem_len); 794 }; 795 796 const base_val, const base_elem_offset, const oob_ty = switch (base_strat) { 797 .direct => |direct| .{ direct.val, 0, direct.val.typeOf(zcu) }, 798 .index => |index| restructure_info: { 799 const elem_ty = index.val.typeOf(zcu).childType(zcu); 800 const elem_off = elem_ty.arrayBase(zcu)[1] * index.elem_index; 801 break :restructure_info .{ index.val, elem_off, elem_ty }; 802 }, 803 .flat_index => |flat| .{ flat.val, flat.flat_elem_index, flat.val.typeOf(zcu) }, 804 .reinterpret => break :restructure_array, 805 else => unreachable, 806 }; 807 const val_one_ty, const val_count = base_val.typeOf(zcu).arrayBase(zcu); 808 if (.ok != try sema.coerceInMemoryAllowed(block, val_one_ty, store_one_ty, true, zcu.getTarget(), src, src, null)) { 809 break :restructure_array; 810 } 811 if (base_elem_offset + extra_base_index + store_count > val_count) return .{ .out_of_bounds = oob_ty }; 812 813 if (store_ty.zigTypeTag(zcu) == .array) { 814 const skip = store_ty.childType(zcu).arrayBase(zcu)[1] * array_offset; 815 return .{ .flat_index = .{ 816 .alloc = base_strat.alloc(), 817 .val = base_val, 818 .flat_elem_index = skip + base_elem_offset + extra_base_index, 819 } }; 820 } 821 822 // `base_val` must be an array, since otherwise the "direct reinterpret" logic above noticed it. 823 assert(base_val.typeOf(zcu).zigTypeTag(zcu) == .array); 824 825 var index: u64 = base_elem_offset + extra_base_index; 826 const arr_val, const arr_index = (try recursiveIndex(sema, base_val, &index)).?; 827 return .{ .index = .{ 828 .alloc = base_strat.alloc(), 829 .val = arr_val, 830 .elem_index = arr_index, 831 } }; 832 } 833 834 // We need to reinterpret memory, which is only possible if neither the store 835 // type nor the type of the base value have an ill-defined layout. 836 837 if (!store_ty.hasWellDefinedLayout(zcu)) { 838 return .{ .needed_well_defined = store_ty }; 839 } 840 841 var cur_val: *MutableValue, var cur_offset: u64 = switch (base_strat) { 842 .direct => |direct| .{ direct.val, 0 }, 843 // It's okay to do `abiSize` - the comptime-only case will be caught below. 844 .index => |index| .{ index.val, index.elem_index * index.val.typeOf(zcu).childType(zcu).abiSize(zcu) }, 845 .flat_index => |flat_index| .{ 846 flat_index.val, 847 // It's okay to do `abiSize` - the comptime-only case will be caught below. 848 flat_index.flat_elem_index * flat_index.val.typeOf(zcu).arrayBase(zcu)[0].abiSize(zcu), 849 }, 850 .reinterpret => |r| .{ r.val, r.byte_offset }, 851 else => unreachable, 852 }; 853 cur_offset += ptr.byte_offset; 854 855 if (!cur_val.typeOf(zcu).hasWellDefinedLayout(zcu)) { 856 return .{ .needed_well_defined = cur_val.typeOf(zcu) }; 857 } 858 859 if (store_ty.zigTypeTag(zcu) == .array and array_offset > 0) { 860 cur_offset += store_ty.childType(zcu).abiSize(zcu) * array_offset; 861 } 862 863 const need_bytes = store_ty.abiSize(zcu); 864 865 if (cur_offset + need_bytes > cur_val.typeOf(zcu).abiSize(zcu)) { 866 return .{ .out_of_bounds = cur_val.typeOf(zcu) }; 867 } 868 869 // In the worst case, we can reinterpret the entire value - however, that's 870 // pretty wasteful. If the memory region we're interested in refers to one 871 // field or array element, let's just look at that. 872 while (true) { 873 const cur_ty = cur_val.typeOf(zcu); 874 switch (cur_ty.zigTypeTag(zcu)) { 875 .noreturn, 876 .type, 877 .comptime_int, 878 .comptime_float, 879 .null, 880 .undefined, 881 .enum_literal, 882 .@"opaque", 883 .spirv, 884 .@"fn", 885 .error_union, 886 => unreachable, // ill-defined layout 887 .int, 888 .float, 889 .bool, 890 .void, 891 .pointer, 892 .error_set, 893 .@"anyframe", 894 .frame, 895 .@"enum", 896 .vector, 897 => break, // terminal types (no sub-values) 898 .optional => break, // this can only be a pointer-like optional so is terminal 899 .array => { 900 const elem_ty = cur_ty.childType(zcu); 901 const elem_size = elem_ty.abiSize(zcu); 902 const elem_idx = cur_offset / elem_size; 903 const next_elem_off = elem_size * (elem_idx + 1); 904 if (cur_offset + need_bytes <= next_elem_off) { 905 // We can look at a single array element. 906 cur_val = try cur_val.elem(pt, sema.arena, @intCast(elem_idx)); 907 cur_offset -= elem_idx * elem_size; 908 } else { 909 break; 910 } 911 }, 912 .@"struct" => switch (cur_ty.containerLayout(zcu)) { 913 .auto => unreachable, // ill-defined layout 914 .@"packed" => break, // let the memory reinterp logic handle this 915 .@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| { 916 const start_off = cur_ty.structFieldOffset(field_idx, zcu); 917 const end_off = start_off + cur_ty.fieldType(field_idx, zcu).abiSize(zcu); 918 if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) { 919 cur_val = try cur_val.elem(pt, sema.arena, field_idx); 920 cur_offset -= start_off; 921 break; 922 } 923 } else break, // pointer spans multiple fields 924 }, 925 .@"union" => switch (cur_ty.containerLayout(zcu)) { 926 .auto => unreachable, // ill-defined layout 927 .@"packed" => break, // let the memory reinterp logic handle this 928 .@"extern" => { 929 // TODO: we have to let the memory reinterp logic handle this for now. 930 // Otherwise, we might traverse into a union field which doesn't allow pointers. 931 // Figure out a solution! 932 if (true) break; 933 try cur_val.unintern(pt, sema.arena, false, false); 934 const payload = switch (cur_val.*) { 935 .un => |un| un.payload, 936 else => unreachable, 937 }; 938 // The payload always has offset 0. If it's big enough 939 // to represent the whole load type, we can use it. 940 if (payload.typeOf(zcu).abiSize(zcu) >= need_bytes) { 941 cur_val = payload; 942 } else { 943 break; 944 } 945 }, 946 }, 947 } 948 } 949 950 // Fast path: check again if we're now at the type we want to store. 951 // If so, we can use the `direct` strategy. 952 if (cur_offset == 0 and cur_val.typeOf(zcu).toIntern() == store_ty.toIntern()) { 953 return .{ .direct = .{ 954 .alloc = base_strat.alloc(), 955 .val = cur_val, 956 } }; 957 } 958 959 return .{ .reinterpret = .{ 960 .alloc = base_strat.alloc(), 961 .val = cur_val, 962 .byte_offset = cur_offset, 963 } }; 964 } 965 966 /// Given a potentially-nested array value, recursively flatten all of its elements into the given 967 /// output array. The result can be used by `unflattenArray` to restructure array values. 968 fn flattenArray( 969 sema: *Sema, 970 val: MutableValue, 971 skip: *u64, 972 next_idx: *u64, 973 out: []InternPool.Index, 974 ) Allocator.Error!void { 975 if (next_idx.* == out.len) return; 976 977 const zcu = sema.pt.zcu; 978 979 const ty = val.typeOf(zcu); 980 const base_elem_count = ty.arrayBase(zcu)[1]; 981 if (skip.* >= base_elem_count) { 982 skip.* -= base_elem_count; 983 return; 984 } 985 986 if (ty.zigTypeTag(zcu) != .array) { 987 out[@intCast(next_idx.*)] = (try val.intern(sema.pt, sema.arena)).toIntern(); 988 next_idx.* += 1; 989 return; 990 } 991 992 const arr_base_elem_count = ty.childType(zcu).arrayBase(zcu)[1]; 993 for (0..@intCast(ty.arrayLen(zcu))) |elem_idx| { 994 // Optimization: the `getElem` here may be expensive since we might intern an 995 // element of the `bytes` representation, so avoid doing it unnecessarily. 996 if (next_idx.* == out.len) return; 997 if (skip.* >= arr_base_elem_count) { 998 skip.* -= arr_base_elem_count; 999 continue; 1000 } 1001 try flattenArray(sema, try val.getElem(sema.pt, elem_idx), skip, next_idx, out); 1002 } 1003 if (ty.sentinel(zcu)) |s| { 1004 try flattenArray(sema, .{ .interned = s.toIntern() }, skip, next_idx, out); 1005 } 1006 } 1007 1008 /// Given a sequence of non-array elements, "unflatten" them into the given array type. 1009 /// Asserts that values of `elems` are in-memory coercible to the array base type of `ty`. 1010 fn unflattenArray( 1011 sema: *Sema, 1012 ty: Type, 1013 elems: []const InternPool.Index, 1014 next_idx: *u64, 1015 ) Allocator.Error!Value { 1016 const pt = sema.pt; 1017 const zcu = pt.zcu; 1018 const arena = sema.arena; 1019 1020 if (ty.zigTypeTag(zcu) != .array) { 1021 const val = Value.fromInterned(elems[@intCast(next_idx.*)]); 1022 next_idx.* += 1; 1023 return pt.getCoerced(val, ty); 1024 } 1025 1026 const elem_ty = ty.childType(zcu); 1027 const buf = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu))); 1028 for (buf) |*elem| { 1029 elem.* = (try unflattenArray(sema, elem_ty, elems, next_idx)).toIntern(); 1030 } 1031 if (ty.sentinel(zcu) != null) { 1032 // TODO: validate sentinel 1033 _ = try unflattenArray(sema, elem_ty, elems, next_idx); 1034 } 1035 return pt.aggregateValue(ty, buf); 1036 } 1037 1038 /// Given a `MutableValue` representing a potentially-nested array, treats `index` as an index into 1039 /// the array's base type. For instance, given a [3][3]T, the index 5 represents 'val[1][2]'. 1040 /// The final level of array is not dereferenced. This allows use sites to use `setElem` to prevent 1041 /// unnecessary `MutableValue` representation changes. 1042 fn recursiveIndex( 1043 sema: *Sema, 1044 mv: *MutableValue, 1045 index: *u64, 1046 ) !?struct { *MutableValue, u64 } { 1047 const pt = sema.pt; 1048 1049 const ty = mv.typeOf(pt.zcu); 1050 assert(ty.zigTypeTag(pt.zcu) == .array); 1051 1052 const ty_base_elems = ty.arrayBase(pt.zcu)[1]; 1053 if (index.* >= ty_base_elems) { 1054 index.* -= ty_base_elems; 1055 return null; 1056 } 1057 1058 const elem_ty = ty.childType(pt.zcu); 1059 if (elem_ty.zigTypeTag(pt.zcu) != .array) { 1060 assert(index.* < ty.arrayLenIncludingSentinel(pt.zcu)); // should be handled by initial check 1061 return .{ mv, index.* }; 1062 } 1063 1064 for (0..@intCast(ty.arrayLenIncludingSentinel(pt.zcu))) |elem_index| { 1065 if (try recursiveIndex(sema, try mv.elem(pt, sema.arena, elem_index), index)) |result| { 1066 return result; 1067 } 1068 } 1069 unreachable; // should be handled by initial check 1070 } 1071 1072 fn checkComptimeVarStore( 1073 sema: *Sema, 1074 block: *Block, 1075 src: LazySrcLoc, 1076 alloc_index: ComptimeAllocIndex, 1077 ) !void { 1078 const runtime_index = sema.getComptimeAlloc(alloc_index).runtime_index; 1079 if (@backingInt(runtime_index) < @backingInt(block.runtime_index)) { 1080 if (block.runtime_cond) |cond_src| { 1081 const msg = msg: { 1082 const msg = try sema.errMsg(src, "store to comptime variable depends on runtime condition", .{}); 1083 errdefer msg.destroy(sema.gpa); 1084 try sema.errNote(cond_src, msg, "runtime condition here", .{}); 1085 break :msg msg; 1086 }; 1087 return sema.failWithOwnedErrorMsg(block, msg); 1088 } 1089 if (block.runtime_loop) |loop_src| { 1090 const msg = msg: { 1091 const msg = try sema.errMsg(src, "cannot store to comptime variable in non-inline loop", .{}); 1092 errdefer msg.destroy(sema.gpa); 1093 try sema.errNote(loop_src, msg, "non-inline loop here", .{}); 1094 break :msg msg; 1095 }; 1096 return sema.failWithOwnedErrorMsg(block, msg); 1097 } 1098 unreachable; 1099 } 1100 } 1101 1102 const std = @import("std"); 1103 const assert = std.debug.assert; 1104 const Allocator = std.mem.Allocator; 1105 1106 const InternPool = @import("../InternPool.zig"); 1107 const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; 1108 const Sema = @import("../Sema.zig"); 1109 const Block = Sema.Block; 1110 const MutableValue = @import("../mutable_value.zig").MutableValue; 1111 const Type = @import("../Type.zig"); 1112 const Value = @import("../Value.zig"); 1113 const Zcu = @import("../Zcu.zig"); 1114 const LazySrcLoc = Zcu.LazySrcLoc; 1115