authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-31 18:14:31+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:09+00:00
logb8997f871fc63cee28d94168330d84f177543f2c
tree5ae9d230d0ca3dc50947ca1fda3c69d68f2172c9
parent38fdced8bb255d3460afefaacce9dc89dab97def
signaturelock-open Commit is signed but in an unrecognized format.

Sema: clean up and fix alignment handling


8 files changed, 743 insertions(+), 901 deletions(-)

src/Sema.zig+433-502
......@@ -3595,7 +3595,20 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
35953595 };
35963596 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
35973597 },
3598 .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(),
3598 .elem => |idx| ptr: {
3599 const parent_ptr_val: Value = .fromInterned(decl_parent_ptr);
3600 if (parent_ptr_val.typeOf(zcu).childType(zcu).zigTypeTag(zcu) == .vector) {
3601 const elem_ptr_ty: Type = .fromInterned(new_ptr_ty);
3602 // Vectors are a bit weird; see logic in `elemPtrVector`.
3603 if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
3604 break :ptr (try pt.getCoerced(parent_ptr_val, elem_ptr_ty)).toIntern();
3605 } else {
3606 const bit_offset = idx * @divExact(elem_ptr_ty.childType(zcu).bitSize(zcu), 8);
3607 break :ptr (try parent_ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt)).toIntern();
3608 }
3609 }
3610 break :ptr (try parent_ptr_val.ptrElem(idx, pt)).toIntern();
3611 },
35993612 };
36003613 try ptr_mapping.put(air_ptr, new_ptr);
36013614 }
......@@ -4540,10 +4553,7 @@ fn validateStructInit(
45404553 };
45414554
45424555 const field_src = init_src; // TODO better source location
4543 const default_field_ptr = if (struct_ty.isTuple(zcu))
4544 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
4545 else
4546 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
4556 const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
45474557 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
45484558 try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
45494559 }
......@@ -4959,11 +4969,11 @@ pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!
49594969 .ty = array_ty.toIntern(),
49604970 .storage = .{ .bytes = string },
49614971 } });
4962 return sema.uavRef(val);
4972 return sema.uavRef(.fromInterned(val));
49634973}
49644974
4965fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref {
4966 return Air.internedToRef(try sema.pt.refValue(val));
4975fn uavRef(sema: *Sema, val: Value) CompileError!Air.Inst.Ref {
4976 return .fromValue(try sema.pt.uavValue(val));
49674977}
49684978
49694979fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -6226,7 +6236,7 @@ fn popErrorReturnTrace(
62266236 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
62276237 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
62286238 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
6229 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
6239 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty);
62306240 try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
62316241 } else if (is_non_error == null) {
62326242 // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need
......@@ -6251,7 +6261,7 @@ fn popErrorReturnTrace(
62516261 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
62526262 const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
62536263 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
6254 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
6264 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty);
62556265 try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
62566266 _ = try then_block.addBr(cond_block_inst, .void_value);
62576267
......@@ -8196,23 +8206,10 @@ fn zirOptionalPayload(
81968206 const operand_ty = sema.typeOf(operand);
81978207 const result_ty = switch (operand_ty.zigTypeTag(zcu)) {
81988208 .optional => operand_ty.optionalChild(zcu),
8199 .pointer => t: {
8200 if (operand_ty.ptrSize(zcu) != .c) {
8201 return sema.failWithExpectedOptionalType(block, src, operand_ty);
8202 }
8203 // TODO https://github.com/ziglang/zig/issues/6597
8204 if (true) break :t operand_ty;
8205 const ptr_info = operand_ty.ptrInfo(zcu);
8206 break :t try pt.ptrType(.{
8207 .child = ptr_info.child,
8208 .flags = .{
8209 .alignment = ptr_info.flags.alignment,
8210 .is_const = ptr_info.flags.is_const,
8211 .is_volatile = ptr_info.flags.is_volatile,
8212 .is_allowzero = ptr_info.flags.is_allowzero,
8213 .address_space = ptr_info.flags.address_space,
8214 },
8215 });
8209 // TODO: https://github.com/ziglang/zig/issues/6597 will eliminate this branch so that we only need to handle optionals.
8210 .pointer => switch (operand_ty.ptrSize(zcu)) {
8211 .c => operand_ty, // if `ptr` is a `[*c]T`, then `ptr.?` is also a `[*c]T`
8212 .one, .many, .slice => return sema.failWithExpectedOptionalType(block, src, operand_ty),
82168213 },
82178214 else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
82188215 };
......@@ -10322,10 +10319,10 @@ fn analyzeSwitchBlock(
1032210319 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
1032310320 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
1032410321 const payload_ref: Air.Inst.Ref = payload_ref: {
10325 const item_val: InternPool.Index = switch (operand_ty.zigTypeTag(zcu)) {
10322 const item_val: Value = switch (operand_ty.zigTypeTag(zcu)) {
1032610323 .@"union" => item_val: {
1032710324 if (maybe_operand_opv) |operand_opv| {
10328 break :item_val zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val;
10325 break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val);
1032910326 }
1033010327 assert(union_originally); // operand type must be union, otherwise it would be an OPV type here
1033110328 assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture
......@@ -10362,10 +10359,10 @@ fn analyzeSwitchBlock(
1036210359 validated_switch.else_err_ty,
1036310360 );
1036410361 },
10365 else => item_opv.toIntern(),
10362 else => item_opv,
1036610363 };
1036710364 break :payload_ref switch (capture) {
10368 .by_val => .fromIntern(item_val),
10365 .by_val => .fromValue(item_val),
1036910366 .by_ref => try sema.uavRef(item_val),
1037010367 .none => unreachable,
1037110368 };
......@@ -12198,7 +12195,7 @@ fn analyzeSwitchPayloadCapture(
1219812195 return case_block.addStructFieldVal(operand_val, field_index, field_ty);
1219912196 }
1220012197 } else if (capture_by_ref) {
12201 return sema.uavRef(item_val.toIntern());
12198 return sema.uavRef(item_val);
1220212199 } else {
1220312200 return kind.inline_ref;
1220412201 }
......@@ -12280,37 +12277,14 @@ fn analyzeSwitchPayloadCapture(
1228012277
1228112278 // By-reference captures have some further restrictions which make them easier to emit
1228212279 if (capture_by_ref) {
12283 const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu);
12280 const operand_ptr_ty = sema.typeOf(operand_ptr);
1228412281 const capture_ptr_ty = resolve: {
1228512282 // By-ref captures of hetereogeneous types are only allowed if all field
1228612283 // pointer types are peer resolvable to each other.
1228712284 // We need values to run PTR on, so make a bunch of undef constants.
1228812285 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
12289 for (field_indices, dummy_captures) |field_idx, *dummy| {
12290 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12291 const field_ptr_ty = try pt.ptrType(.{
12292 .child = field_ty.toIntern(),
12293 .flags = .{
12294 .is_const = operand_ptr_info.flags.is_const,
12295 .is_volatile = operand_ptr_info.flags.is_volatile,
12296 .address_space = operand_ptr_info.flags.address_space,
12297 // TODO MLUGG: double-check this. and, um, EVERYWHERE we do ptr alignment...
12298 .alignment = a: {
12299 if (operand_ty.explicitFieldAlignment(field_idx, zcu) == .none and
12300 operand_ptr_info.flags.alignment == .none)
12301 {
12302 break :a .none;
12303 }
12304
12305 const union_align = switch (operand_ptr_info.flags.alignment) {
12306 .none => operand_ty.abiAlignment(zcu),
12307 else => |a| a,
12308 };
12309 const field_align = operand_ty.resolvedFieldAlignment(field_idx, zcu);
12310 break :a .minStrict(union_align, field_align);
12311 },
12312 },
12313 });
12286 for (field_indices, dummy_captures) |field_index, *dummy| {
12287 const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt);
1231412288 dummy.* = try pt.undefRef(field_ptr_ty);
1231512289 }
1231612290 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
......@@ -13696,7 +13670,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1369613670 element_vals[elem_i] = coerced_elem_val.toIntern();
1369713671 }
1369813672 return sema.addConstantMaybeRef(
13699 (try pt.aggregateValue(result_ty, element_vals)).toIntern(),
13673 try pt.aggregateValue(result_ty, element_vals),
1370013674 ptr_addrspace != null,
1370113675 );
1370213676 } else break :rs rhs_src;
......@@ -14094,7 +14068,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1409414068 }
1409514069 break :v try pt.aggregateValue(result_ty, element_vals);
1409614070 };
14097 return sema.addConstantMaybeRef(val.toIntern(), ptr_addrspace != null);
14071 return sema.addConstantMaybeRef(val, ptr_addrspace != null);
1409814072 }
1409914073
1410014074 try sema.requireRuntimeBlock(block, src, lhs_src);
......@@ -15270,7 +15244,7 @@ fn analyzeArithmetic(
1527015244 };
1527115245
1527215246 try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src);
15273 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
15247 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, rhs_src);
1527415248 },
1527515249 }
1527615250 }
......@@ -15370,7 +15344,6 @@ fn analyzePtrArithmetic(
1537015344 ptr: Air.Inst.Ref,
1537115345 uncasted_offset: Air.Inst.Ref,
1537215346 air_tag: Air.Inst.Tag,
15373 ptr_src: LazySrcLoc,
1537415347 offset_src: LazySrcLoc,
1537515348) CompileError!Air.Inst.Ref {
1537615349 // TODO if the operand is comptime-known to be negative, or is a negative int,
......@@ -15378,12 +15351,14 @@ fn analyzePtrArithmetic(
1537815351 const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src);
1537915352 const pt = sema.pt;
1538015353 const zcu = pt.zcu;
15381 const opt_ptr_val = sema.resolveValue(ptr);
15382 const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
1538315354 const ptr_ty = sema.typeOf(ptr);
1538415355 const ptr_info = ptr_ty.ptrInfo(zcu);
1538515356 assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c);
1538615357
15358 const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, offset_src, offset)) |val| off: {
15359 break :off val.toUnsignedInt(zcu);
15360 } else null;
15361
1538715362 const elem_ty: Type = .fromInterned(ptr_info.child);
1538815363 elem_ty.assertHasLayout(zcu);
1538915364
......@@ -15395,70 +15370,36 @@ fn analyzePtrArithmetic(
1539515370 else => {},
1539615371 }
1539715372
15398 const new_ptr_ty = t: {
15399 // Calculate the new pointer alignment.
15400 // This code is duplicated in `Type.elemPtrType`.
15401 if (ptr_info.flags.alignment == .none) {
15402 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
15403 break :t ptr_ty;
15404 }
15405 // If the addend is not a comptime-known value we can still count on
15406 // it being a multiple of the type size.
15407 const elem_size = elem_ty.abiSize(zcu);
15408 const addend = if (opt_off_val) |off_val| a: {
15409 const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(zcu));
15410 break :a elem_size * off_int;
15411 } else elem_size;
15412
15413 // The resulting pointer is aligned to the lcd between the offset (an
15414 // arbitrary number) and the alignment factor (always a power of two,
15415 // non zero).
15416 const new_align: Alignment = @enumFromInt(@min(
15417 @ctz(addend),
15418 @intFromEnum(ptr_info.flags.alignment),
15419 ));
15420 assert(new_align != .none);
15421
15422 break :t try pt.ptrType(.{
15423 .child = ptr_info.child,
15424 .sentinel = ptr_info.sentinel,
15425 .flags = .{
15426 .size = ptr_info.flags.size,
15427 .alignment = new_align,
15428 .is_const = ptr_info.flags.is_const,
15429 .is_volatile = ptr_info.flags.is_volatile,
15430 .is_allowzero = ptr_info.flags.is_allowzero,
15431 .address_space = ptr_info.flags.address_space,
15432 },
15433 });
15434 };
15373 const elem_ptr_ty = try ptr_ty.elemPtrType(maybe_index, pt);
15374 // `elem_ptr_ty` is a single-item pointer, but we want a many-item or C pointer, and to preserve
15375 // any input sentinel.
15376 const new_ptr_ty = try pt.ptrType(info: {
15377 var info = elem_ptr_ty.ptrInfo(zcu);
15378 info.flags.size = ptr_info.flags.size;
15379 info.sentinel = ptr_info.sentinel;
15380 break :info info;
15381 });
1543515382
15436 const runtime_src = rs: {
15437 if (opt_ptr_val) |ptr_val| {
15438 if (opt_off_val) |offset_val| {
15439 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
15440
15441 const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(zcu));
15442 if (offset_int == 0) return ptr;
15443 if (air_tag == .ptr_sub) {
15444 const elem_size = elem_ty.abiSize(zcu);
15445 const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty);
15446 return Air.internedToRef(new_ptr_val.toIntern());
15447 } else {
15448 const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty);
15449 return Air.internedToRef(new_ptr_val.toIntern());
15450 }
15451 } else break :rs offset_src;
15452 } else break :rs ptr_src;
15453 };
15383 ct: {
15384 const ptr_val = sema.resolveValue(ptr) orelse break :ct;
15385 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
15386 const index = maybe_index orelse break :ct;
15387
15388 if (index == 0) return ptr;
15389 if (air_tag == .ptr_sub) {
15390 const elem_size = elem_ty.abiSize(zcu);
15391 return .fromValue(try sema.ptrSubtract(block, op_src, ptr_val, index * elem_size, new_ptr_ty));
15392 } else {
15393 return .fromValue(try pt.getCoerced(try ptr_val.ptrElem(index, pt), new_ptr_ty));
15394 }
15395 }
1545415396
15455 try sema.requireRuntimeBlock(block, op_src, runtime_src);
1545615397 try sema.checkLogicalPtrOperation(block, op_src, ptr_ty);
1545715398
1545815399 return block.addInst(.{
1545915400 .tag = air_tag,
1546015401 .data = .{ .ty_pl = .{
15461 .ty = Air.internedToRef(new_ptr_ty.toIntern()),
15402 .ty = .fromType(new_ptr_ty),
1546215403 .payload = try sema.addExtra(Air.Bin{
1546315404 .lhs = ptr,
1546415405 .rhs = offset,
......@@ -16222,6 +16163,11 @@ fn zirBuiltinSrc(
1622216163 return Air.internedToRef((try pt.aggregateValue(src_loc_ty, &fields)).toIntern());
1622316164}
1622416165
16166/// MLUGG TODO: once this branch is in a more stable state, I need to make a language change so that
16167/// `std.builtin.Type` makes all `alignment` fields `?usize` instead of `comptime_int`, to prevent
16168/// explicit alignment annotations from sneaking in without the user requesting any; but doing that
16169/// right now would be really annoying because it would break the base compiler. I need to have the
16170/// compiler more-or-less fully migrated first.
1622516171fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1622616172 const pt = sema.pt;
1622716173 const zcu = pt.zcu;
......@@ -16726,17 +16672,21 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1672616672 } });
1672716673 };
1672816674
16675 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
16676
1672916677 const alignment = switch (layout) {
16730 .auto, .@"extern" => ty.resolvedFieldAlignment(field_index, zcu),
16678 .auto, .@"extern" => switch (ty.explicitFieldAlignment(field_index, zcu)) {
16679 .none => field_ty.abiAlignment(zcu),
16680 else => |a| a,
16681 },
1673116682 .@"packed" => .none,
1673216683 };
1673316684
16734 const field_ty = union_obj.field_types.get(ip)[field_index];
1673516685 const union_field_fields = .{
1673616686 // name: [:0]const u8,
1673716687 name_val,
1673816688 // type: type,
16739 field_ty,
16689 field_ty.toIntern(),
1674016690 // alignment: comptime_int,
1674116691 (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(),
1674216692 };
......@@ -16895,7 +16845,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1689516845 const opt_default_val: ?Value = if (field_default == .none) null else .fromInterned(field_default);
1689616846 const default_val_ptr = try sema.optRefValue(opt_default_val);
1689716847 const alignment = switch (struct_type.layout) {
16898 .auto, .@"extern" => ty.resolvedFieldAlignment(field_index, zcu),
16848 .auto, .@"extern" => switch (ty.explicitFieldAlignment(field_index, zcu)) {
16849 .none => field_ty.defaultStructFieldAlignment(struct_type.layout, zcu),
16850 else => |a| a,
16851 },
1689916852 .@"packed" => .none,
1690016853 };
1690116854
......@@ -18425,8 +18378,7 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
1842518378 const init_ref = try sema.coerce(block, init_ty, empty_ref, src);
1842618379
1842718380 if (is_byref) {
18428 const init_val = sema.resolveValue(init_ref).?;
18429 return sema.uavRef(init_val.toIntern());
18381 return sema.uavRef(sema.resolveValue(init_ref).?);
1843018382 } else {
1843118383 return init_ref;
1843218384 }
......@@ -18648,7 +18600,7 @@ fn zirStructInit(
1864818600 }));
1864918601 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src);
1865018602 const final_val = sema.resolveValue(final_val_inst).?;
18651 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
18603 return sema.addConstantMaybeRef(final_val, is_ref);
1865218604 }
1865318605
1865418606 if (resolved_ty.comptimeOnly(zcu)) {
......@@ -18789,7 +18741,7 @@ fn finishStructInit(
1878918741 }
1879018742 const struct_val = try pt.aggregateValue(struct_ty, elems);
1879118743 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18792 return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
18744 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
1879318745 },
1879418746 .@"packed" => {
1879518747 const buf = try sema.arena.alloc(u8, (struct_ty.bitSize(zcu) + 7) / 8);
......@@ -18808,7 +18760,7 @@ fn finishStructInit(
1880818760 error.OutOfMemory => |e| return e,
1880918761 };
1881018762 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18811 return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
18763 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
1881218764 },
1881318765 };
1881418766
......@@ -19000,7 +18952,7 @@ fn structInitAnon(
1900018952
1900118953 _ = opt_runtime_index orelse {
1900218954 const struct_val = try pt.aggregateValue(struct_ty, values);
19003 return sema.addConstantMaybeRef(struct_val.toIntern(), is_ref);
18955 return sema.addConstantMaybeRef(struct_val, is_ref);
1900418956 };
1900518957
1900618958 if (is_ref) {
......@@ -19137,7 +19089,7 @@ fn zirArrayInit(
1913719089 const arr_val = try pt.aggregateValue(array_ty, elem_vals);
1913819090 const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src);
1913919091 const result_val = (sema.resolveValue(result_ref)).?;
19140 return sema.addConstantMaybeRef(result_val.toIntern(), is_ref);
19092 return sema.addConstantMaybeRef(result_val, is_ref);
1914119093 };
1914219094
1914319095 if (is_ref) {
......@@ -19261,7 +19213,7 @@ fn arrayInitAnon(
1926119213
1926219214 const runtime_src = opt_runtime_src orelse {
1926319215 const tuple_val = try pt.aggregateValue(tuple_ty, values);
19264 return sema.addConstantMaybeRef(tuple_val.toIntern(), is_ref);
19216 return sema.addConstantMaybeRef(tuple_val, is_ref);
1926519217 };
1926619218
1926719219 try sema.requireRuntimeBlock(block, src, runtime_src);
......@@ -19304,8 +19256,8 @@ fn arrayInitAnon(
1930419256 return block.addAggregateInit(tuple_ty, element_refs);
1930519257}
1930619258
19307fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref {
19308 return if (is_ref) sema.uavRef(val) else Air.internedToRef(val);
19259fn addConstantMaybeRef(sema: *Sema, val: Value, is_ref: bool) !Air.Inst.Ref {
19260 return if (is_ref) sema.uavRef(val) else .fromValue(val);
1930919261}
1931019262
1931119263fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -23401,125 +23353,74 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
2340123353 const field_ptr = sema.resolveInst(extra.field_ptr);
2340223354 const field_ptr_ty = sema.typeOf(field_ptr);
2340323355 try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty);
23404 const field_ptr_info = field_ptr_ty.ptrInfo(zcu);
2340523356
23406 var actual_parent_ptr_info: InternPool.Key.PtrType = .{
23407 .child = parent_ty.toIntern(),
23408 .flags = .{
23409 .alignment = parent_ptr_ty.ptrAlignment(zcu),
23410 .is_const = field_ptr_info.flags.is_const,
23411 .is_volatile = field_ptr_info.flags.is_volatile,
23412 .is_allowzero = field_ptr_info.flags.is_allowzero,
23413 .address_space = field_ptr_info.flags.address_space,
23414 },
23415 .packed_offset = parent_ptr_info.packed_offset,
23416 };
23417 const field_ty = parent_ty.fieldType(field_index, zcu);
23418 var actual_field_ptr_info: InternPool.Key.PtrType = .{
23419 .child = field_ty.toIntern(),
23420 .flags = .{
23421 .alignment = field_ptr_ty.ptrAlignment(zcu),
23422 .is_const = field_ptr_info.flags.is_const,
23423 .is_volatile = field_ptr_info.flags.is_volatile,
23424 .is_allowzero = field_ptr_info.flags.is_allowzero,
23425 .address_space = field_ptr_info.flags.address_space,
23426 },
23427 .packed_offset = field_ptr_info.packed_offset,
23428 };
23429 switch (parent_ty.containerLayout(zcu)) {
23430 .auto => {
23431 actual_parent_ptr_info.flags.alignment = parent_ty.resolvedFieldAlignment(field_index, zcu);
23432 actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
23433 actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
23434 },
23435 .@"extern" => {
23436 const field_offset = parent_ty.structFieldOffset(field_index, zcu);
23437 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0)
23438 Alignment.fromLog2Units(@ctz(field_offset))
23439 else
23440 actual_field_ptr_info.flags.alignment);
23441
23442 actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
23443 actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
23444 },
23445 .@"packed" => {
23446 const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
23447 (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) -
23448 actual_field_ptr_info.packed_offset.bit_offset), 8) catch
23449 return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{});
23450 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0)
23451 Alignment.fromLog2Units(@ctz(byte_offset))
23452 else
23453 actual_field_ptr_info.flags.alignment);
23454 },
23455 }
23357 const hypothetical_field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_index, pt);
23358 const casted_field_ptr = try sema.ptrCastFull(
23359 block,
23360 flags,
23361 inst_src,
23362 field_ptr,
23363 field_ptr_src,
23364 hypothetical_field_ptr_ty,
23365 "@fieldParentPtr",
23366 );
2345623367
23457 const actual_field_ptr_ty = try pt.ptrType(actual_field_ptr_info);
23458 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src);
23459 const actual_parent_ptr_ty = try pt.ptrType(actual_parent_ptr_info);
23368 const unaligned_parent_ptr_ty = try pt.ptrType(info: {
23369 var info = parent_ptr_ty.ptrInfo(zcu);
23370 info.flags.alignment = hypothetical_field_ptr_ty.ptrAlignment(zcu);
23371 break :info info;
23372 });
2346023373
23461 const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: {
23462 switch (parent_ty.zigTypeTag(zcu)) {
23463 .@"struct" => switch (parent_ty.containerLayout(zcu)) {
23464 .auto => {},
23465 .@"extern" => {
23466 const byte_offset = parent_ty.structFieldOffset(field_index, zcu);
23467 const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
23468 break :result Air.internedToRef(parent_ptr_val.toIntern());
23469 },
23470 .@"packed" => {
23471 // Logic lifted from type computation above - I'm just assuming it's correct.
23472 // `catch unreachable` since error case handled above.
23473 const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
23474 zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) -
23475 actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable;
23476 const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
23477 break :result Air.internedToRef(parent_ptr_val.toIntern());
23478 },
23479 },
23480 .@"union" => switch (parent_ty.containerLayout(zcu)) {
23481 .auto => {},
23482 .@"extern", .@"packed" => {
23483 // For an extern or packed union, just coerce the pointer.
23484 const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty);
23485 break :result Air.internedToRef(parent_ptr_val.toIntern());
23486 },
23487 },
23374 const unaligned_parent_ptr: Air.Inst.Ref = if (try sema.resolveDefinedValue(
23375 block,
23376 field_ptr_src,
23377 casted_field_ptr,
23378 )) |field_ptr_val| switch (parent_ty.containerLayout(zcu)) {
23379 .@"packed" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)),
23380 .@"extern" => switch (parent_ty.zigTypeTag(zcu)) {
23381 .@"struct" => .fromValue(try sema.ptrSubtract(
23382 block,
23383 field_ptr_src,
23384 field_ptr_val,
23385 parent_ty.structFieldOffset(field_index, zcu),
23386 unaligned_parent_ptr_ty,
23387 )),
23388 .@"union" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)),
2348823389 else => unreachable,
23489 }
23490
23491 const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
23492 const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
23493 .ptr => |ptr| ptr,
23494 else => break :opt_field null,
23495 };
23496 if (ptr.byte_offset != 0) break :opt_field null;
23497 break :opt_field switch (ptr.base_addr) {
23498 .field => |field| field,
23499 else => null,
23390 },
23391 .auto => result: {
23392 const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
23393 const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
23394 .ptr => |ptr| ptr,
23395 else => break :opt_field null,
23396 };
23397 if (ptr.byte_offset != 0) break :opt_field null;
23398 break :opt_field switch (ptr.base_addr) {
23399 .field => |field| field,
23400 else => null,
23401 };
2350023402 };
23501 };
2350223403
23503 const field = opt_field orelse {
23504 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
23505 };
23404 const field = opt_field orelse {
23405 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
23406 };
2350623407
23507 if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
23508 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
23509 }
23408 if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
23409 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
23410 }
2351023411
23511 if (field.index != field_index) {
23512 return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
23513 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
23514 });
23515 }
23516 break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src);
23412 if (field.index != field_index) {
23413 return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
23414 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
23415 });
23416 }
23417 break :result .fromValue(try pt.getCoerced(.fromInterned(field.base), unaligned_parent_ptr_ty));
23418 },
2351723419 } else result: {
23518 try sema.requireRuntimeBlock(block, inst_src, field_ptr_src);
2351923420 break :result try block.addInst(.{
2352023421 .tag = .field_parent_ptr,
2352123422 .data = .{ .ty_pl = .{
23522 .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()),
23423 .ty = .fromType(unaligned_parent_ptr_ty),
2352323424 .payload = try block.sema.addExtra(Air.FieldParentPtr{
2352423425 .field_ptr = casted_field_ptr,
2352523426 .field_index = @intCast(field_index),
......@@ -23527,14 +23428,61 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
2352723428 } },
2352823429 });
2352923430 };
23530 return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr");
23431
23432 // There's one more error condition: if the hypothetical field pointer type has a lower
23433 // alignment than the parent pointer type, then we need an `@alignCast`. Note that the earlier
23434 // `ptrCastFull` may *also* have "used" the `@alignCast`; that would be a case where the field
23435 // is naturally less aligned than the rest of the struct, *and* the field pointer is itself
23436 // underaligned compared to the field alignment. For example, `struct { a: u32, b: u16 }` with
23437 // a field pointer of type `*align(1) u16`.
23438 switch (hypothetical_field_ptr_ty.ptrAlignment(zcu).order(parent_ptr_ty.ptrAlignment(zcu))) {
23439 .gt => unreachable, // getting a field pointer can never increase alignment
23440 .eq => return unaligned_parent_ptr,
23441 .lt => if (flags.align_cast) {
23442 // Go through `ptrCastFull` for the safety check.
23443 return sema.ptrCastFull(
23444 block,
23445 flags,
23446 inst_src,
23447 unaligned_parent_ptr,
23448 inst_src,
23449 parent_ptr_ty,
23450 "@fieldParentPtr",
23451 );
23452 } else return sema.failWithOwnedErrorMsg(block, msg: {
23453 const msg = try sema.errMsg(inst_src, "@fieldParentPtr increases pointer alignment", .{});
23454 errdefer msg.destroy(sema.gpa);
23455 try sema.errNote(inst_src, msg, "parent pointer type '{f}' has alignment '{d}'", .{
23456 parent_ptr_ty.fmt(pt),
23457 parent_ptr_ty.abiAlignment(zcu),
23458 });
23459 if (parent_ty.isTuple(zcu)) {
23460 try sema.errNote(field_ptr_src, msg, "tuple field '{d}' limits alignment to '{d}'", .{
23461 field_index,
23462 field_ptr_ty.ptrAlignment(zcu),
23463 });
23464 } else {
23465 try sema.errNote(parent_ty.srcLoc(zcu), msg, "{t} field '{f}' limits alignment to '{d}'", .{
23466 parent_ty.zigTypeTag(zcu),
23467 switch (parent_ty.zigTypeTag(zcu)) {
23468 .@"struct" => parent_ty.structFieldName(field_index, zcu).unwrap().?.fmt(ip),
23469 .@"union" => parent_ty.unionTagTypeHypothetical(zcu).enumFieldName(field_index, zcu).fmt(ip),
23470 else => unreachable,
23471 },
23472 field_ptr_ty.ptrAlignment(zcu),
23473 });
23474 }
23475 try sema.errNote(inst_src, msg, "use @alignCast to assert pointer alignment", .{});
23476 break :msg msg;
23477 }),
23478 }
2353123479}
2353223480
2353323481fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value {
2353423482 const pt = sema.pt;
2353523483 const zcu = pt.zcu;
2353623484 if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty);
23537 var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
23485 const ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
2353823486 .undef => return sema.failWithUseOfUndef(block, src, null),
2353923487 .ptr => |ptr| ptr,
2354023488 else => unreachable,
......@@ -23547,9 +23495,11 @@ fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte
2354723495 break :msg msg;
2354823496 });
2354923497 }
23550 ptr.byte_offset -= byte_subtract;
23551 ptr.ty = new_ty.toIntern();
23552 return Value.fromInterned(try pt.intern(.{ .ptr = ptr }));
23498 return Value.fromInterned(try pt.intern(.{ .ptr = .{
23499 .ty = new_ty.toIntern(),
23500 .base_addr = ptr.base_addr,
23501 .byte_offset = ptr.byte_offset - byte_subtract,
23502 } }));
2355323503}
2355423504
2355523505fn zirMinMax(
......@@ -24186,8 +24136,8 @@ fn zirMemcpy(
2418624136
2418724137 // ok1: dest >= src + len
2418824138 // ok2: src >= dest + len
24189 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
24190 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
24139 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src);
24140 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, src);
2419124141 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
2419224142 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
2419324143 const ok = try block.addBinOp(.bool_or, ok1, ok2);
......@@ -25402,21 +25352,36 @@ fn addSafetyCheckSentinelMismatch(
2540225352 const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern());
2540325353
2540425354 const ptr_ty = sema.typeOf(ptr);
25405 const actual_sentinel = if (ptr_ty.isSlice(zcu))
25406 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
25407 else blk: {
25408 const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt);
25409 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
25410 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
25411 };
25412
25413 const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: {
25414 const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
25415 break :ok try parent_block.addReduce(eql, .And);
25416 } else ok: {
25417 assert(sentinel_ty.isSelfComparable(zcu, true));
25418 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
25355 const ptr_info = ptr_ty.ptrInfo(zcu);
25356 const actual_sentinel: Air.Inst.Ref = switch (ptr_ty.ptrSize(zcu)) {
25357 .slice => try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index),
25358 .one => s: {
25359 const array_ty: Type = .fromInterned(ptr_info.child);
25360 assert(array_ty.zigTypeTag(zcu) == .array);
25361 assert(array_ty.childType(zcu).toIntern() == sentinel_ty.toIntern());
25362 const many_ptr_ty = try pt.ptrType(.{
25363 .child = sentinel_ty.toIntern(),
25364 .flags = .{
25365 .size = .many,
25366 .is_const = ptr_info.flags.is_const,
25367 .is_volatile = ptr_info.flags.is_volatile,
25368 .is_allowzero = ptr_info.flags.is_allowzero,
25369 .alignment = switch (ptr_info.flags.alignment) {
25370 .none => .none,
25371 else => |ptr_align| .minStrict(ptr_align, sentinel_ty.abiAlignment(zcu)),
25372 },
25373 .address_space = ptr_info.flags.address_space,
25374 },
25375 });
25376 const many_ptr = try parent_block.addBitCast(many_ptr_ty, ptr);
25377 break :s try parent_block.addBinOp(.ptr_elem_val, many_ptr, sentinel_index);
25378 },
25379 .many => unreachable,
25380 .c => unreachable,
2541925381 };
25382 assert(sema.typeOf(actual_sentinel).toIntern() == sentinel_ty.toIntern());
25383 assert(sentinel_ty.isSelfComparable(zcu, true));
25384 const ok = try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
2542025385
2542125386 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{
2542225387 expected_sentinel, actual_sentinel,
......@@ -25676,7 +25641,7 @@ fn fieldVal(
2567625641 .@"struct" => if (is_pointer_to) {
2567725642 // Avoid loading the entire struct by fetching a pointer and loading that
2567825643 try sema.ensureLayoutResolved(inner_ty, src);
25679 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
25644 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
2568025645 return sema.analyzeLoad(block, src, field_ptr, object_src);
2568125646 } else {
2568225647 return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty);
......@@ -25730,7 +25695,7 @@ fn fieldPtr(
2573025695 .array => {
2573125696 if (field_name.eqlSlice("len", ip)) {
2573225697 const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu));
25733 return uavRef(sema, int_val.toIntern());
25698 return uavRef(sema, int_val);
2573425699 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
2573525700 const ptr_info = object_ty.ptrInfo(zcu);
2573625701 const new_ptr_ty = try pt.ptrType(.{
......@@ -25752,6 +25717,7 @@ fn fieldPtr(
2575225717 .child = new_ptr_ty.toIntern(),
2575325718 .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
2575425719 .flags = .{
25720 .size = .one,
2575525721 .alignment = ptr_ptr_info.flags.alignment,
2575625722 .is_const = ptr_ptr_info.flags.is_const,
2575725723 .is_volatile = ptr_ptr_info.flags.is_volatile,
......@@ -25857,10 +25823,10 @@ fn fieldPtr(
2585725823 },
2585825824 else => unreachable,
2585925825 };
25860 return uavRef(sema, try pt.intern(.{ .err = .{
25826 return uavRef(sema, .fromInterned(try pt.intern(.{ .err = .{
2586125827 .ty = err_set_ty.toIntern(),
2586225828 .name = field_name,
25863 } }));
25829 } })));
2586425830 },
2586525831 .@"union" => {
2586625832 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
......@@ -25871,7 +25837,7 @@ fn fieldPtr(
2587125837 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
2587225838 const field_index_u32: u32 = @intCast(field_index);
2587325839 const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32);
25874 return uavRef(sema, idx_val.toIntern());
25840 return uavRef(sema, idx_val);
2587525841 }
2587625842 }
2587725843 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
......@@ -25886,7 +25852,7 @@ fn fieldPtr(
2588625852 };
2588725853 const field_index_u32: u32 = @intCast(field_index);
2588825854 const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32);
25889 return uavRef(sema, idx_val.toIntern());
25855 return uavRef(sema, idx_val);
2589025856 },
2589125857 .@"struct", .@"opaque" => {
2589225858 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
......@@ -25903,7 +25869,7 @@ fn fieldPtr(
2590325869 else
2590425870 object_ptr;
2590525871 try sema.ensureLayoutResolved(inner_ty, src);
25906 const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
25872 const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
2590725873 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
2590825874 return field_ptr;
2590925875 },
......@@ -26190,7 +26156,6 @@ fn structFieldPtr(
2619026156 field_name: InternPool.NullTerminatedString,
2619126157 field_name_src: LazySrcLoc,
2619226158 struct_ty: Type,
26193 initializing: bool,
2619426159) CompileError!Air.Inst.Ref {
2619526160 const pt = sema.pt;
2619626161 const zcu = pt.zcu;
......@@ -26199,23 +26164,24 @@ fn structFieldPtr(
2619926164 assert(struct_ty.zigTypeTag(zcu) == .@"struct");
2620026165 struct_ty.assertHasLayout(zcu);
2620126166
26202 if (struct_ty.isTuple(zcu)) {
26167 const field_index: u32 = if (struct_ty.isTuple(zcu)) field_index: {
2620326168 if (field_name.eqlSlice("len", ip)) {
2620426169 const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu));
2620526170 return sema.analyzeRef(block, src, len_inst);
2620626171 }
26207 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
26208 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
26209 }
26210
26211 const struct_type = zcu.typeToStruct(struct_ty).?;
26212
26213 const field_index = struct_type.nameIndex(ip, field_name) orelse
26214 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
26172 break :field_index try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
26173 } else field_index: {
26174 const struct_type = zcu.typeToStruct(struct_ty).?;
26175 break :field_index struct_type.nameIndex(ip, field_name) orelse {
26176 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
26177 };
26178 };
2621526179
2621626180 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty);
2621726181}
2621826182
26183/// Supports both structs and unions.
26184///
2621926185/// Asserts that the layout of `struct_ty` is already resolved.
2622026186fn structFieldPtrByIndex(
2622126187 sema: *Sema,
......@@ -26227,82 +26193,23 @@ fn structFieldPtrByIndex(
2622726193) CompileError!Air.Inst.Ref {
2622826194 const pt = sema.pt;
2622926195 const zcu = pt.zcu;
26230 const ip = &zcu.intern_pool;
2623126196
2623226197 struct_ty.assertHasLayout(zcu);
26233
26234 const struct_type = zcu.typeToStruct(struct_ty).?;
26235 const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index);
26236
26237 // Comptime fields are handled later
26238 if (!field_is_comptime) {
26239 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
26240 const val = try struct_ptr_val.ptrField(field_index, pt);
26241 return Air.internedToRef(val.toIntern());
26242 }
26243 }
26244
26245 const field_ty = struct_type.field_types.get(ip)[field_index];
2624626198 const struct_ptr_ty = sema.typeOf(struct_ptr);
26247 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
26248 assert(struct_ptr_ty_info.child == struct_ty.toIntern());
26249
26250 var ptr_ty_data: InternPool.Key.PtrType = .{
26251 .child = field_ty,
26252 .flags = .{
26253 .is_const = struct_ptr_ty_info.flags.is_const,
26254 .is_volatile = struct_ptr_ty_info.flags.is_volatile,
26255 .address_space = struct_ptr_ty_info.flags.address_space,
26256 },
26257 };
2625826199
26259 const parent_align = if (struct_ptr_ty_info.flags.alignment != .none)
26260 struct_ptr_ty_info.flags.alignment
26261 else
26262 struct_ty.abiAlignment(zcu);
26263
26264 if (struct_type.layout == .@"packed") {
26265 assert(!field_is_comptime);
26266 const packed_offset = struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt);
26267 ptr_ty_data.flags.alignment = parent_align;
26268 ptr_ty_data.packed_offset = packed_offset;
26269 } else if (struct_type.layout == .@"extern") {
26270 assert(!field_is_comptime);
26271 // For extern structs, field alignment might be bigger than type's
26272 // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
26273 // second field is aligned as u32.
26274 ptr_ty_data.flags.alignment = a: {
26275 const field_off = struct_ty.structFieldOffset(field_index, zcu);
26276 if (field_off == 0) break :a struct_ptr_ty_info.flags.alignment;
26277 const true_field_align: Alignment = .fromLog2Units(@ctz(field_off));
26278 if (struct_ptr_ty_info.flags.alignment == .none and
26279 true_field_align == Type.fromInterned(field_ty).abiAlignment(zcu))
26280 {
26281 break :a .none;
26282 }
26283 break :a .minStrict(true_field_align, parent_align);
26284 };
26285 } else {
26286 // Our alignment is capped at the field alignment.
26287 ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
26288 struct_ty.explicitFieldAlignment(field_index, zcu)
26289 else
26290 struct_ty.resolvedFieldAlignment(field_index, zcu).min(parent_align);
26291 }
26292
26293 const ptr_field_ty = try pt.ptrType(ptr_ty_data);
26294
26295 if (field_is_comptime) {
26296 assert(struct_type.field_defaults.get(ip)[field_index] != .none);
26297 const val = try pt.intern(.{ .ptr = .{
26298 .ty = ptr_field_ty.toIntern(),
26299 .base_addr = .{ .comptime_field = struct_type.field_defaults.get(ip)[field_index] },
26200 if (struct_ty.structFieldIsComptime(field_index, zcu)) {
26201 const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt);
26202 return .fromIntern(try pt.intern(.{ .ptr = .{
26203 .ty = field_ptr_ty.toIntern(),
26204 .base_addr = .{ .comptime_field = struct_ty.structFieldDefaultValue(field_index, zcu).?.toIntern() },
2630026205 .byte_offset = 0,
26301 } });
26302 return Air.internedToRef(val);
26206 } }));
26207 } else if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
26208 return .fromValue(try struct_ptr_val.ptrField(field_index, pt));
26209 } else {
26210 const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt);
26211 return block.addStructFieldPtr(struct_ptr, field_index, field_ptr_ty);
2630326212 }
26304
26305 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
2630626213}
2630726214
2630826215fn structFieldVal(
......@@ -26438,29 +26345,11 @@ fn unionFieldPtr(
2643826345 assert(union_ty.zigTypeTag(zcu) == .@"union");
2643926346 union_ty.assertHasLayout(zcu);
2644026347
26441 const union_ptr_ty = sema.typeOf(union_ptr);
26442 const union_ptr_info = union_ptr_ty.ptrInfo(zcu);
2644326348 const union_obj = zcu.typeToUnion(union_ty).?;
26349 const tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
26350
2644426351 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
2644526352 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
26446 const ptr_field_ty = try pt.ptrType(.{
26447 .child = field_ty.toIntern(),
26448 .flags = .{
26449 .is_const = union_ptr_info.flags.is_const,
26450 .is_volatile = union_ptr_info.flags.is_volatile,
26451 .address_space = union_ptr_info.flags.address_space,
26452 .alignment = a: {
26453 if (union_obj.layout != .auto) break :a union_ptr_info.flags.alignment;
26454 if (union_ptr_info.flags.alignment == .none) {
26455 break :a union_ty.explicitFieldAlignment(field_index, zcu);
26456 }
26457 const field_align = union_ty.resolvedFieldAlignment(field_index, zcu);
26458 break :a union_ptr_info.flags.alignment.min(field_align);
26459 },
26460 },
26461 .packed_offset = union_ptr_info.packed_offset,
26462 });
26463 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_type).enumFieldIndex(field_name, zcu).?);
2646426353
2646526354 if (initializing and field_ty.classify(zcu) == .no_possible_value) {
2646626355 const msg = msg: {
......@@ -26484,23 +26373,17 @@ fn unionFieldPtr(
2648426373 break :ct;
2648526374 }
2648626375 // Store to the union to initialize the tag.
26487 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), enum_field_index);
26488 const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
26489 const new_union_val = try pt.unionValue(union_ty, field_tag, try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty));
26376 const field_tag = try pt.enumValueFieldIndex(tag_ty, field_index);
26377 const payload_val = try field_ty.onePossibleValue(pt) orelse try pt.undefValue(field_ty);
26378 const new_union_val = try pt.unionValue(union_ty, field_tag, payload_val);
2649026379 try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty);
2649126380 } else {
26492 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
26493 break :ct;
26494 if (union_val.isUndef(zcu)) {
26495 return sema.failWithUseOfUndef(block, src, null);
26496 }
26497 const un = ip.indexToKey(union_val.toIntern()).un;
26498 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), enum_field_index);
26499 const tag_matches = un.tag == field_tag.toIntern();
26500 if (!tag_matches) {
26381 const union_val = try sema.pointerDeref(block, src, union_ptr_val, union_ptr_val.typeOf(zcu)) orelse break :ct;
26382 if (union_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
26383 const active_index = tag_ty.enumTagFieldIndex(union_val.unionTag(zcu).?, zcu).?;
26384 if (active_index != field_index) {
2650126385 const msg = msg: {
26502 const active_index = Type.fromInterned(union_obj.enum_tag_type).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
26503 const active_field_name = Type.fromInterned(union_obj.enum_tag_type).enumFieldName(active_index, zcu);
26386 const active_field_name = tag_ty.enumFieldName(active_index, zcu);
2650426387 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
2650526388 field_name.fmt(ip),
2650626389 active_field_name.fmt(ip),
......@@ -26520,11 +26403,10 @@ fn unionFieldPtr(
2652026403 // If the union has a tag, we must either set or or safety check it depending on `initializing`.
2652126404 tag: {
2652226405 if (union_ty.containerLayout(zcu) != .auto) break :tag;
26523 const tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
2652426406 if (tag_ty.classify(zcu) == .one_possible_value) break :tag;
2652526407 // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
2652626408 // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
26527 const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
26409 const want_tag = try pt.enumValueFieldIndex(tag_ty, field_index);
2652826410 if (initializing) {
2652926411 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
2653026412 try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
......@@ -26540,7 +26422,9 @@ fn unionFieldPtr(
2654026422 _ = try block.addNoOp(.unreach);
2654126423 return .unreachable_value;
2654226424 }
26543 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
26425
26426 const field_ptr_ty = try sema.typeOf(union_ptr).fieldPtrType(field_index, pt);
26427 return block.addStructFieldPtr(union_ptr, field_index, field_ptr_ty);
2654426428}
2654526429
2654626430fn unionFieldVal(
......@@ -26628,13 +26512,9 @@ fn elemPtr(
2662826512 try sema.ensureLayoutResolved(indexable_ty, src);
2662926513
2663026514 const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
26631 .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
26632 .@"struct" => blk: {
26633 // Tuple field access.
26634 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
26635 const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
26636 break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
26637 },
26515 .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
26516 .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
26517 .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),
2663826518 else => {
2663926519 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
2664026520 try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src);
......@@ -26672,21 +26552,21 @@ fn elemPtrOneLayerOnly(
2667226552 .many, .c => {
2667326553 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
2667426554 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
26555 const maybe_index: ?u64 = if (maybe_index_val) |val| val.toUnsignedInt(zcu) else null;
2667526556 ct: {
2667626557 const ptr_val = maybe_ptr_val orelse break :ct;
26677 const index_val = maybe_index_val orelse break :ct;
26678 const index: usize = @intCast(index_val.toUnsignedInt(zcu));
26679 const elem_ptr = try ptr_val.ptrElem(index, pt);
26680 return Air.internedToRef(elem_ptr.toIntern());
26558 const index: usize = @intCast(maybe_index orelse break :ct);
26559 return .fromValue(try ptr_val.ptrElem(index, pt));
2668126560 }
2668226561
2668326562 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
26684 const result_ty = try indexable_ty.elemPtrType(null, pt);
26563
26564 const result_ty = try indexable_ty.elemPtrType(maybe_index, pt);
2668526565
2668626566 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src);
2668726567 try sema.validateRuntimeValue(block, indexable_src, indexable);
2668826568
26689 if (result_ty.childType(zcu).abiSize(zcu) == 0) {
26569 if (child_ty.abiSize(zcu) == 0) {
2669026570 // zero-bit child type; just bitcast the pointer
2669126571 return block.addBitCast(result_ty, indexable);
2669226572 }
......@@ -26695,13 +26575,9 @@ fn elemPtrOneLayerOnly(
2669526575 },
2669626576 .one => {
2669726577 const elem_ptr = switch (child_ty.zigTypeTag(zcu)) {
26698 .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
26699 .@"struct" => blk: {
26700 assert(child_ty.isTuple(zcu));
26701 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
26702 const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
26703 break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
26704 },
26578 .vector => try sema.elemPtrVector(block, indexable_src, indexable, elem_index_src, elem_index, init),
26579 .array => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
26580 .@"struct" => try sema.tupleElemPtr(block, indexable_src, indexable, elem_index, elem_index_src),
2670526581 else => unreachable, // Guaranteed by checkIndexable
2670626582 };
2670726583 try sema.checkKnownAllocPtr(block, indexable, elem_ptr);
......@@ -26746,10 +26622,8 @@ fn elemVal(
2674626622 const index: usize = @intCast(index_val.toUnsignedInt(zcu));
2674726623 const many_ptr_ty = try pt.manyConstPtrType(child_ty);
2674826624 const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty);
26749 const elem_ptr_ty = try pt.singleConstPtrType(child_ty);
2675026625 const elem_ptr_val = try many_ptr_val.ptrElem(index, pt);
26751 const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct;
26752 return Air.internedToRef((try pt.getCoerced(elem_val, child_ty)).toIntern());
26626 return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), indexable_src);
2675326627 }
2675426628
2675526629 if (try child_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
......@@ -26824,70 +26698,38 @@ fn validateRuntimeElemAccess(
2682426698 }
2682526699}
2682626700
26827/// Asserts that the layout of the tuple type is already resolved.
26828fn tupleFieldPtr(
26701/// Validates `elem_index`, and returns a pointer to that field using `structFieldPtrByIndex`.
26702///
26703/// Asserts that the type of `tuple_ptr` is a single-item pointer whose child type is a tuple.
26704fn tupleElemPtr(
2682926705 sema: *Sema,
2683026706 block: *Block,
26831 tuple_ptr_src: LazySrcLoc,
26707 src: LazySrcLoc,
2683226708 tuple_ptr: Air.Inst.Ref,
26833 field_index_src: LazySrcLoc,
26834 field_index: u32,
26835 init: bool,
26709 elem_index: Air.Inst.Ref,
26710 elem_index_src: LazySrcLoc,
2683626711) CompileError!Air.Inst.Ref {
2683726712 const pt = sema.pt;
2683826713 const zcu = pt.zcu;
2683926714 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
26840 const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu);
26841 const tuple_ty: Type = .fromInterned(tuple_ptr_info.child);
26842 const field_count = tuple_ty.structFieldCount(zcu);
26843
26844 tuple_ty.assertHasLayout(zcu);
26715 assert(tuple_ptr_ty.isSinglePointer(zcu));
26716 const tuple_ty = tuple_ptr_ty.childType(zcu);
26717 assert(tuple_ty.isTuple(zcu));
2684526718
26719 const field_count = tuple_ty.structFieldCount(zcu);
2684626720 if (field_count == 0) {
26847 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
26721 return sema.fail(block, src, "indexing into empty tuple is not allowed", .{});
2684826722 }
2684926723
26850 if (field_index >= field_count) {
26851 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
26852 field_index, field_count,
26724 const elem_index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
26725 const index = elem_index_val.getUnsignedInt(zcu);
26726 if (index == null or index.? >= field_count) {
26727 return sema.fail(block, elem_index_src, "index '{f}' out of bounds of tuple '{f}'", .{
26728 elem_index_val.fmtValueSema(pt, sema), tuple_ty.fmt(pt),
2685326729 });
2685426730 }
2685526731
26856 const field_ty = tuple_ty.fieldType(field_index, zcu);
26857 const ptr_field_ty = try pt.ptrType(.{
26858 .child = field_ty.toIntern(),
26859 .flags = .{
26860 .is_const = tuple_ptr_info.flags.is_const,
26861 .is_volatile = tuple_ptr_info.flags.is_volatile,
26862 .address_space = tuple_ptr_info.flags.address_space,
26863 .alignment = a: {
26864 if (tuple_ptr_info.flags.alignment == .none) break :a .none;
26865 // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned.
26866 const tuple_align = tuple_ptr_info.flags.alignment;
26867 const field_align = field_ty.abiAlignment(zcu);
26868 break :a tuple_align.min(field_align);
26869 },
26870 },
26871 });
26872
26873 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| {
26874 return Air.internedToRef((try pt.intern(.{ .ptr = .{
26875 .ty = ptr_field_ty.toIntern(),
26876 .base_addr = .{ .comptime_field = default_val.toIntern() },
26877 .byte_offset = 0,
26878 } })));
26879 }
26880
26881 if (sema.resolveValue(tuple_ptr)) |tuple_ptr_val| {
26882 const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt);
26883 return Air.internedToRef(field_ptr_val.toIntern());
26884 }
26885
26886 if (!init) {
26887 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
26888 }
26889
26890 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
26732 return sema.structFieldPtrByIndex(block, src, tuple_ptr, @intCast(index.?), tuple_ty);
2689126733}
2689226734
2689326735fn tupleField(
......@@ -26994,7 +26836,102 @@ fn elemValArray(
2699426836 return block.addBinOp(.array_elem_val, array, elem_index);
2699526837}
2699626838
26997/// Asserts that the layout of the array or vector is already resolved.
26839fn elemPtrVector(
26840 sema: *Sema,
26841 block: *Block,
26842 vector_ptr_src: LazySrcLoc,
26843 vector_ptr: Air.Inst.Ref,
26844 elem_index_src: LazySrcLoc,
26845 elem_index: Air.Inst.Ref,
26846 init: bool,
26847) CompileError!Air.Inst.Ref {
26848 const pt = sema.pt;
26849 const zcu = pt.zcu;
26850 const vector_ptr_ty = sema.typeOf(vector_ptr);
26851 const vector_ty = vector_ptr_ty.childType(zcu);
26852 assert(vector_ty.zigTypeTag(zcu) == .vector);
26853 const vector_len = vector_ty.vectorLen(zcu);
26854
26855 if (vector_len == 0) {
26856 return sema.fail(block, vector_ptr_src, "cannot index into empty vector", .{});
26857 }
26858
26859 const maybe_vector_ptr_val = sema.resolveValue(vector_ptr);
26860 // The index must not be undefined since it can be out of bounds.
26861 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse {
26862 return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
26863 };
26864 const index = index_val.toUnsignedInt(zcu);
26865 if (index >= vector_len) {
26866 return sema.fail(block, elem_index_src, "index {d} outside vector of length {d}", .{ index, vector_len });
26867 }
26868
26869 const elem_ty = vector_ty.childType(zcu);
26870 const elem_bits = elem_ty.bitSize(zcu);
26871 // Exiting this block means the operation is a runtime one.
26872 const elem_ptr_ty: Type = if (elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits)) elem_ptr_ty: {
26873 // Use a packed pointer (i.e. vector_index != 0)
26874 const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu);
26875 const elem_ptr_ty = try pt.ptrType(.{
26876 .child = elem_ty.toIntern(),
26877 .flags = .{
26878 .size = .one,
26879 .alignment = vector_ptr_info.flags.alignment,
26880 .is_const = vector_ptr_info.flags.is_const,
26881 .is_volatile = vector_ptr_info.flags.is_volatile,
26882 .is_allowzero = vector_ptr_info.flags.is_allowzero,
26883 .address_space = vector_ptr_info.flags.address_space,
26884 .vector_index = @enumFromInt(index),
26885 },
26886 .packed_offset = .{
26887 .host_size = @intCast(vector_len),
26888 .bit_offset = 0,
26889 },
26890 });
26891 if (maybe_vector_ptr_val) |ptr_val| {
26892 if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty);
26893 return .fromValue(try pt.getCoerced(ptr_val, elem_ptr_ty));
26894 }
26895 break :elem_ptr_ty elem_ptr_ty;
26896 } else elem_ptr_ty: {
26897 // Use a normal pointer (i.e. vector_index == 0)
26898 const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu);
26899 const elem_ptr_ty = try pt.ptrType(.{
26900 .child = elem_ty.toIntern(),
26901 .flags = .{
26902 .size = .one,
26903 // TODO: this logic was ported from old code, but it's bogus. This entire block will
26904 // go away when https://github.com/ziglang/zig/issues/24061 is implemented anyway.
26905 .alignment = switch (vector_ptr_info.flags.alignment) {
26906 .none => .none,
26907 else => |vec_align| switch (index * elem_ty.abiSize(zcu)) {
26908 0 => vec_align,
26909 else => |byte_offset| .minStrict(vec_align, .fromLog2Units(@ctz(byte_offset))),
26910 },
26911 },
26912 .is_const = vector_ptr_info.flags.is_const,
26913 .is_volatile = vector_ptr_info.flags.is_volatile,
26914 .is_allowzero = vector_ptr_info.flags.is_allowzero,
26915 .address_space = vector_ptr_info.flags.address_space,
26916 },
26917 });
26918 if (maybe_vector_ptr_val) |ptr_val| {
26919 if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty);
26920 const bit_offset = index * @divExact(elem_ty.bitSize(zcu), 8);
26921 return .fromValue(try ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt));
26922 }
26923 break :elem_ptr_ty elem_ptr_ty;
26924 };
26925
26926 if (!init) {
26927 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, vector_ty, vector_ptr_src);
26928 try sema.validateRuntimeValue(block, vector_ptr_src, vector_ptr);
26929 }
26930
26931 return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty);
26932}
26933
26934/// Asserts that the layout of the array is already resolved.
2699826935fn elemPtrArray(
2699926936 sema: *Sema,
2700026937 block: *Block,
......@@ -27009,19 +26946,21 @@ fn elemPtrArray(
2700926946 const pt = sema.pt;
2701026947 const zcu = pt.zcu;
2701126948 const array_ptr_ty = sema.typeOf(array_ptr);
26949 assert(array_ptr_ty.ptrSize(zcu) == .one);
2701226950 const array_ty = array_ptr_ty.childType(zcu);
26951 assert(array_ty.zigTypeTag(zcu) == .array);
2701326952 const array_sent = array_ty.sentinel(zcu) != null;
2701426953 const array_len = array_ty.arrayLen(zcu);
2701526954 const array_len_s = array_len + @intFromBool(array_sent);
2701626955
2701726956 if (array_len_s == 0) {
27018 return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
26957 return sema.fail(block, array_ptr_src, "cannot index into empty array", .{});
2701926958 }
2702026959
2702126960 const maybe_undef_array_ptr_val = sema.resolveValue(array_ptr);
2702226961 // The index must not be undefined since it can be out of bounds.
27023 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
27024 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(zcu));
26962 const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
26963 const index = index_val.toUnsignedInt(zcu);
2702526964 if (index >= array_len_s) {
2702626965 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
2702726966 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
......@@ -27029,20 +26968,15 @@ fn elemPtrArray(
2702926968 break :o index;
2703026969 } else null;
2703126970
27032 if (offset == null and array_ty.zigTypeTag(zcu) == .vector) {
27033 return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
27034 }
27035
2703626971 array_ty.assertHasLayout(zcu);
27037 const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt);
26972 const elem_ptr_ty = try array_ptr_ty.elemPtrType(maybe_index, pt);
2703826973
2703926974 if (maybe_undef_array_ptr_val) |array_ptr_val| {
2704026975 if (array_ptr_val.isUndef(zcu)) {
2704126976 return pt.undefRef(elem_ptr_ty);
2704226977 }
27043 if (offset) |index| {
27044 const elem_ptr = try array_ptr_val.ptrElem(index, pt);
27045 return Air.internedToRef(elem_ptr.toIntern());
26978 if (maybe_index) |index| {
26979 return .fromValue(try array_ptr_val.ptrElem(index, pt));
2704626980 }
2704726981 }
2704826982
......@@ -27052,7 +26986,7 @@ fn elemPtrArray(
2705226986 }
2705326987
2705426988 // Runtime check is only needed if unable to comptime check.
27055 if (oob_safety and block.wantSafety() and offset == null) {
26989 if (oob_safety and block.wantSafety() and maybe_index == null) {
2705626990 const len_inst = try pt.intRef(.usize, array_len);
2705726991 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
2705826992 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
......@@ -27075,9 +27009,9 @@ fn elemValSlice(
2707527009 const pt = sema.pt;
2707627010 const zcu = pt.zcu;
2707727011 const slice_ty = sema.typeOf(slice);
27012 assert(slice_ty.isSlice(zcu));
2707827013 const slice_sent = slice_ty.sentinel(zcu) != null;
2707927014 const elem_ty = slice_ty.childType(zcu);
27080 var runtime_src = slice_src;
2708127015
2708227016 elem_ty.assertHasLayout(zcu);
2708327017
......@@ -27087,7 +27021,6 @@ fn elemValSlice(
2708727021 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
2708827022
2708927023 if (maybe_slice_val) |slice_val| {
27090 runtime_src = elem_index_src;
2709127024 const slice_len = slice_val.sliceLen(zcu);
2709227025 const slice_len_s = slice_len + @intFromBool(slice_sent);
2709327026 if (slice_len_s == 0) {
......@@ -27099,12 +27032,8 @@ fn elemValSlice(
2709927032 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
2710027033 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
2710127034 }
27102 const elem_ptr_ty = try slice_ty.elemPtrType(index, pt);
2710327035 const elem_ptr_val = try slice_val.ptrElem(index, pt);
27104 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
27105 return Air.internedToRef(elem_val.toIntern());
27106 }
27107 runtime_src = slice_src;
27036 return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), slice_src);
2710827037 }
2710927038 }
2711027039
......@@ -27138,17 +27067,19 @@ fn elemPtrSlice(
2713827067 const pt = sema.pt;
2713927068 const zcu = pt.zcu;
2714027069 const slice_ty = sema.typeOf(slice);
27070 assert(slice_ty.isSlice(zcu));
2714127071 const slice_sent = slice_ty.sentinel(zcu) != null;
27142
27143 slice_ty.childType(zcu).assertHasLayout(zcu);
27072 const elem_ty = slice_ty.childType(zcu);
27073 elem_ty.assertHasLayout(zcu);
2714427074
2714527075 const maybe_undef_slice_val = sema.resolveValue(slice);
2714627076 // The index must not be undefined since it can be out of bounds.
27147 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
27148 break :o try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(zcu));
27077 const offset: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
27078 break :o index_val.toUnsignedInt(zcu);
2714927079 } else null;
2715027080
2715127081 const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt);
27082 assert(elem_ptr_ty.childType(zcu).toIntern() == elem_ty.toIntern());
2715227083
2715327084 if (maybe_undef_slice_val) |slice_val| {
2715427085 if (slice_val.isUndef(zcu)) {
......@@ -27157,15 +27088,14 @@ fn elemPtrSlice(
2715727088 const slice_len = slice_val.sliceLen(zcu);
2715827089 const slice_len_s = slice_len + @intFromBool(slice_sent);
2715927090 if (slice_len_s == 0) {
27160 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
27091 return sema.fail(block, slice_src, "cannot index into empty slice", .{});
2716127092 }
2716227093 if (offset) |index| {
2716327094 if (index >= slice_len_s) {
2716427095 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
2716527096 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
2716627097 }
27167 const elem_ptr_val = try slice_val.ptrElem(index, pt);
27168 return Air.internedToRef(elem_ptr_val.toIntern());
27098 return .fromValue(try slice_val.ptrElem(index, pt));
2716927099 }
2717027100 }
2717127101
......@@ -27182,7 +27112,7 @@ fn elemPtrSlice(
2718227112 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
2718327113 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
2718427114 }
27185 if (slice_ty.childType(zcu).abiSize(zcu) == 0) {
27115 if (elem_ty.abiSize(zcu) == 0) {
2718627116 // zero-bit child type; just extract the pointer and bitcast it
2718727117 const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice);
2718827118 return block.addBitCast(elem_ptr_ty, slice_ptr);
......@@ -27546,7 +27476,7 @@ fn coerceExtra(
2754627476 .sentinel = dest_info.sentinel,
2754727477 });
2754827478 const empty_array_val = try pt.aggregateValue(empty_array_ty, &.{});
27549 const empty_array_ptr = try sema.uavRef(empty_array_val.toIntern());
27479 const empty_array_ptr = try sema.uavRef(empty_array_val);
2755027480 return sema.coerceArrayPtrToSlice(block, dest_ty, empty_array_ptr, inst_src);
2755127481 }
2755227482
......@@ -28499,7 +28429,6 @@ pub fn coerceInMemoryAllowed(
2849928429 const field_count = dest_ty.structFieldCount(zcu);
2850028430 for (0..field_count) |field_idx| {
2850128431 if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple;
28502 if (dest_ty.resolvedFieldAlignment(field_idx, zcu) != src_ty.resolvedFieldAlignment(field_idx, zcu)) break :tuple;
2850328432 const dest_field_ty = dest_ty.fieldType(field_idx, zcu);
2850428433 const src_field_ty = src_ty.fieldType(field_idx, zcu);
2850528434 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null);
......@@ -29953,12 +29882,14 @@ pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index:
2995329882fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
2995429883 const pt = sema.pt;
2995529884 const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque);
29956 return Value.fromInterned(try pt.intern(.{ .opt = .{
29957 .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
29958 .val = if (opt_val) |val| (try pt.getCoerced(
29959 Value.fromInterned(try pt.refValue(val.toIntern())),
29960 ptr_anyopaque_ty,
29961 )).toIntern() else .none,
29885 const opt_ptr_anyopaque_ty = try pt.optionalType(ptr_anyopaque_ty.toIntern());
29886 return .fromInterned(try pt.intern(.{ .opt = .{
29887 .ty = opt_ptr_anyopaque_ty.toIntern(),
29888 .val = payload: {
29889 const val = opt_val orelse break :payload .none;
29890 const ptr_val = try pt.getCoerced(try pt.uavValue(val), ptr_anyopaque_ty);
29891 break :payload ptr_val.toIntern();
29892 },
2996229893 } }));
2996329894}
2996429895
......@@ -30078,7 +30009,7 @@ fn analyzeRef(
3007830009 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
3007930010 .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav),
3008030011 .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav),
30081 else => return uavRef(sema, val.toIntern()),
30012 else => return uavRef(sema, val),
3008230013 }
3008330014 }
3008430015
......@@ -30528,7 +30459,7 @@ fn analyzeSlice(
3052830459 } else ptr_or_slice;
3052930460
3053030461 const start = try sema.coerce(block, .usize, uncasted_start, start_src);
30531 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
30462 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src);
3053230463 const new_ptr_ty = sema.typeOf(new_ptr);
3053330464
3053430465 // true if and only if the end index of the slice, implicitly or explicitly, equals
......@@ -30666,7 +30597,7 @@ fn analyzeSlice(
3066630597 break :msg msg;
3066730598 });
3066830599 }
30669 return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
30600 return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src);
3067030601 };
3067130602
3067230603 const sentinel = s: {
......@@ -32118,12 +32049,12 @@ fn resolvePeerTypesInner(
3211832049
3211932050 ptr_info.flags.alignment = a: {
3212032051 // If both alignments are implicit, the result alignment is implicit.
32121 // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]u32'
32052 // e.g. '*u32' + '*c_uint' -> '*u32'
3212232053 if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) {
3212332054 break :a .none;
3212432055 }
3212532056 // Otherwise (if either alignment is explicit), the result alignment is explicit.
32126 // e.g. '[*c]u32' + '[*c]align(4) c_uint' -> '[*c]align(4) u32'
32057 // e.g. '*u32' + '*align(4) c_uint' -> '*align(4) u32'
3212732058 const cur_align = switch (ptr_info.flags.alignment) {
3212832059 .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu),
3212932060 else => ptr_info.flags.alignment,
......@@ -33583,7 +33514,7 @@ fn notePathToComptimeAllocPtr(
3358333514 else => {}, // there will be another stage
3358433515 }
3358533516
33586 const derivation = try comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema);
33517 const derivation = try comptime_ptr.pointerDerivation(arena, pt, sema);
3358733518
3358833519 var second_path_aw: std.Io.Writer.Allocating = .init(arena);
3358933520 defer second_path_aw.deinit();
src/Type.zig+215-138
......@@ -2337,35 +2337,11 @@ pub fn fieldType(ty: Type, index: usize, zcu: *const Zcu) Type {
23372337 return .fromInterned(types.get(ip)[index]);
23382338}
23392339
2340// TODO MLUGG: clean up doc comments and usages of `{resolved,explicit}FieldAlignment`
2341
2342/// Returns the alignment of the given struct, tuple, or union field.
2343/// Asserts that the layout of `ty` is resolved. Asserts that `ty` is not packed.
2344/// Never returns `.none`, even if the field's alignment was not specified.
2345pub fn resolvedFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
2346 switch (ty.explicitFieldAlignment(index, zcu)) {
2347 .none => {},
2348 else => |explicit| return explicit,
2349 }
2350 const ip = &zcu.intern_pool;
2351 return switch (ip.indexToKey(ty.toIntern())) {
2352 .tuple_type => |tuple| Type.fromInterned(tuple.types.get(ip)[index]).abiAlignment(zcu),
2353 .struct_type => {
2354 assertHasLayout(ty, zcu);
2355 const struct_obj = ip.loadStructType(ty.toIntern());
2356 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[index]);
2357 return field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu);
2358 },
2359 .union_type => {
2360 assertHasLayout(ty, zcu);
2361 const union_obj = ip.loadUnionType(ty.toIntern());
2362 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
2363 return field_ty.abiAlignment(zcu);
2364 },
2365 else => unreachable,
2366 };
2367}
2368
2340/// If an alignment was explicitly specified for the given field of the struct or union type `ty`,
2341/// returns that. Otherwise, returns `.none`. This function also supports tuples, for which it
2342/// always returns `.none`.
2343///
2344/// Asserts that the layout of `ty` is resolved, unless `ty` is a tuple.
23692345pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
23702346 const ip = &zcu.intern_pool;
23712347 return switch (ip.indexToKey(ty.toIntern())) {
......@@ -2388,7 +2364,10 @@ pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment
23882364 };
23892365}
23902366
2391/// Returns the alignment a struct field will have if not explicitly specified.
2367/// Returns the alignment a struct field of type `field_ty` will be given if no alignment is
2368/// explicitly specified. However, in an `extern struct`, a higher alignment may be available due
2369/// to the struct's full layout (i.e. a field might coincidentally be more aligned).
2370///
23922371/// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`.
23932372pub fn defaultStructFieldAlignment(
23942373 field_ty: Type,
......@@ -2664,45 +2643,6 @@ pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } {
26642643 return .{ cur_ty, cur_len };
26652644}
26662645
2667/// Returns a bit-pointer with the same value and a new packed offset.
2668pub fn packedStructFieldPtrInfo(
2669 struct_ty: Type,
2670 parent_ptr_ty: Type,
2671 field_idx: u32,
2672 pt: Zcu.PerThread,
2673) InternPool.Key.PtrType.PackedOffset {
2674 comptime assert(Type.packed_struct_layout_version == 2);
2675
2676 const zcu = pt.zcu;
2677 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
2678
2679 var bit_offset: u16 = 0;
2680 var running_bits: u16 = 0;
2681 for (0..struct_ty.structFieldCount(zcu)) |i| {
2682 const f_ty = struct_ty.fieldType(i, zcu);
2683 if (i == field_idx) {
2684 bit_offset = running_bits;
2685 }
2686 running_bits += @intCast(f_ty.bitSize(zcu));
2687 }
2688
2689 const res_host_size: u16, const res_bit_offset: u16 = if (parent_ptr_info.packed_offset.host_size != 0) .{
2690 parent_ptr_info.packed_offset.host_size,
2691 parent_ptr_info.packed_offset.bit_offset + bit_offset,
2692 } else .{
2693 switch (zcu.comp.getZigBackend()) {
2694 else => (running_bits + 7) / 8,
2695 .stage2_x86_64, .stage2_c => @intCast(struct_ty.abiSize(zcu)),
2696 },
2697 bit_offset,
2698 };
2699
2700 return .{
2701 .host_size = res_host_size,
2702 .bit_offset = res_bit_offset,
2703 };
2704}
2705
27062646pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
27072647 const ip = &zcu.intern_pool;
27082648 var most_aligned_field: u32 = 0;
......@@ -2770,88 +2710,225 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
27702710 };
27712711}
27722712
2773/// Returns the type of a pointer to an element.
2774/// Asserts that the type is a pointer, and that the element type is indexable.
2775/// If the element index is comptime-known, it must be passed in `offset`.
2776/// For *@Vector(n, T), return *align(a:b:h:v) T
2777/// For *[N]T, return *T
2778/// For [*]T, returns *T
2779/// For []T, returns *T
2780/// Handles const-ness and address spaces in particular.
2781/// This code is duplicated in `Sema.analyzePtrArithmetic`.
2782/// May perform type resolution and return a transitive `error.AnalysisFail`.
2783/// MLUGG TODO audit this shit
2784pub fn elemPtrType(ptr_ty: Type, offset: ?usize, pt: Zcu.PerThread) !Type {
2713/// Asserts that `ptr_ty` is either a many-item pointer, a slice, a C pointer, or a single pointer
2714/// to array (in other words, a pointer which is indexed by pointer arithmetic), and returns the
2715/// type of the element pointer at the given index.
2716///
2717/// Asserts that the layout of the pointer element type is resolved.
2718///
2719/// If `index` is `null`, the index is an arbitrary runtime-known value.
2720pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error!Type {
27852721 const zcu = pt.zcu;
2786 const ptr_info = ptr_ty.ptrInfo(zcu);
2722 const ip = &zcu.intern_pool;
2723 const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type;
27872724 const elem_ty: Type = switch (ptr_info.flags.size) {
2788 .one => switch (Type.fromInterned(ptr_info.child).zigTypeTag(zcu)) {
2789 .array, .vector => Type.fromInterned(ptr_info.child).childType(zcu),
2790 else => .fromInterned(ptr_info.child),
2791 },
2792 .many, .c, .slice => .fromInterned(ptr_info.child),
2793 };
2794 const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0;
2795 const parent_ty = ptr_ty.childType(zcu);
2796
2797 const VI = InternPool.Key.PtrType.VectorIndex;
2798
2799 const vector_info: struct {
2800 host_size: u16 = 0,
2801 alignment: Alignment = .none,
2802 vector_index: VI = .none,
2803 } = if (parent_ty.isVector(zcu) and ptr_info.flags.size == .one) blk: {
2804 const elem_bits = elem_ty.bitSize(zcu);
2805 if (elem_bits == 0) break :blk .{};
2806 const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits);
2807 if (!is_packed) break :blk .{};
2808
2809 break :blk .{
2810 .host_size = @intCast(parent_ty.arrayLen(zcu)),
2811 .alignment = parent_ty.abiAlignment(zcu),
2812 .vector_index = @enumFromInt(offset.?),
2813 };
2814 } else .{};
2815
2816 const alignment: Alignment = a: {
2817 // Calculate the new pointer alignment.
2818 if (ptr_info.flags.alignment == .none) {
2819 // In case of an ABI-aligned pointer, any pointer arithmetic
2820 // maintains the same ABI-alignedness.
2821 break :a vector_info.alignment;
2822 }
2823 // If the addend is not a comptime-known value we can still count on
2824 // it being a multiple of the type size.
2825 const elem_size = elem_ty.abiSize(zcu);
2826 const addend = if (offset) |off| elem_size * off else elem_size;
2827
2828 // The resulting pointer is aligned to the lcd between the offset (an
2829 // arbitrary number) and the alignment factor (always a power of two,
2830 // non zero).
2831 const new_align: Alignment = @enumFromInt(@min(
2832 @ctz(addend),
2833 ptr_info.flags.alignment.toLog2Units(),
2834 ));
2835 assert(new_align != .none);
2836 break :a new_align;
2725 .slice, .many, .c => .fromInterned(ptr_info.child),
2726 .one => switch (ip.indexToKey(ptr_info.child)) {
2727 .array_type => |array_type| .fromInterned(array_type.child),
2728 else => unreachable,
2729 },
2730 };
2731 elem_ty.assertHasLayout(zcu);
2732 const elem_align: Alignment = switch (elem_ty.classify(zcu)) {
2733 .no_possible_value,
2734 .one_possible_value,
2735 => ptr_info.flags.alignment,
2736
2737 .partially_comptime,
2738 .fully_comptime,
2739 => switch (ptr_info.flags.alignment) {
2740 .none => .none,
2741 else => |array_align| .minStrict(array_align, elem_ty.abiAlignment(zcu)),
2742 },
2743
2744 .runtime => switch (ptr_info.flags.alignment) {
2745 .none => .none,
2746 else => |array_align| elem_align: {
2747 // If the index is runtime-known, use 1 as it gives the minimum possible alignment.
2748 const effective_index = index orelse 1;
2749 if (effective_index == 0) break :elem_align array_align;
2750 const byte_offset = effective_index * elem_ty.abiSize(zcu);
2751 break :elem_align .minStrict(array_align, .fromLog2Units(@ctz(byte_offset)));
2752 },
2753 },
28372754 };
28382755 return pt.ptrType(.{
28392756 .child = elem_ty.toIntern(),
28402757 .flags = .{
2841 .alignment = alignment,
2758 .size = .one,
28422759 .is_const = ptr_info.flags.is_const,
28432760 .is_volatile = ptr_info.flags.is_volatile,
2844 .is_allowzero = is_allowzero,
2761 .is_allowzero = ptr_info.flags.is_allowzero and (index == null or index == 0),
28452762 .address_space = ptr_info.flags.address_space,
2846 .vector_index = vector_info.vector_index,
2847 },
2848 .packed_offset = .{
2849 .host_size = vector_info.host_size,
2850 .bit_offset = 0,
2763 .alignment = elem_align,
28512764 },
28522765 });
28532766}
28542767
2768/// Asserts that `ptr_ty` is a pointer (single-item or C) to a struct, union, tuple, or slice, and
2769/// returns the type of a pointer to the field at `field_index`.
2770///
2771/// Asserts that the layout of the pointer child type is resolved.
2772///
2773/// For slices, `Value.slice_ptr_index` and `Value.slice_len_index` are used for the field index.
2774pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator.Error!Type {
2775 const zcu = pt.zcu;
2776 const ip = &zcu.intern_pool;
2777 const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type;
2778 assert(ptr_info.flags.size == .one or ptr_info.flags.size == .c);
2779 const aggregate_ty: Type = .fromInterned(ptr_info.child);
2780 aggregate_ty.assertHasLayout(zcu);
2781 // We only exit this `switch` for default-layout aggregates, where the field pointer alignment
2782 // is a simple minimum of the aggregate pointer alignment and the field alignment.
2783 // `field_align` is `.none` if there is no explicit alignment annotation.
2784 const field_ty: Type, const field_align: Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
2785 .@"struct" => switch (aggregate_ty.containerLayout(zcu)) {
2786 .auto => field: {
2787 if (aggregate_ty.isTuple(zcu)) {
2788 break :field .{ aggregate_ty.fieldType(field_index, zcu), .none };
2789 }
2790 const struct_obj = ip.loadStructType(aggregate_ty.toIntern());
2791 break :field .{
2792 .fromInterned(struct_obj.field_types.get(ip)[field_index]),
2793 struct_obj.field_aligns.getOrNone(ip, field_index),
2794 };
2795 },
2796 .@"extern" => {
2797 // Field alignment is determined based on the actual field offset. For instance, in
2798 // `extern struct { x: u32, y: u16 }`, the `y` field is 4-byte aligned.
2799 const field_ty = aggregate_ty.fieldType(field_index, zcu);
2800 const field_offset = aggregate_ty.structFieldOffset(field_index, zcu);
2801 const parent_align = switch (ptr_info.flags.alignment) {
2802 .none => aggregate_ty.abiAlignment(zcu),
2803 else => |a| a,
2804 };
2805 const actual_field_align = switch (field_offset) {
2806 0 => parent_align,
2807 else => parent_align.minStrict(.fromLog2Units(@ctz(field_offset))),
2808 };
2809 const field_ptr_align: Alignment = a: {
2810 if (parent_align == .none and
2811 aggregate_ty.explicitFieldAlignment(field_index, zcu) == .none and
2812 actual_field_align == field_ty.abiAlignment(zcu))
2813 {
2814 // There's no user-specified 'align' in sight, and the alignment from the
2815 // field offset matches the field type's natural alignment, so just use a
2816 // default-aligned pointer.
2817 break :a .none;
2818 }
2819 break :a actual_field_align;
2820 };
2821 var field_ptr_info = ptr_info;
2822 field_ptr_info.child = field_ty.toIntern();
2823 field_ptr_info.flags.alignment = field_ptr_align;
2824 return pt.ptrType(field_ptr_info);
2825 },
2826 .@"packed" => {
2827 var field_ptr_info = ptr_info;
2828 if (field_ptr_info.flags.alignment == .none) {
2829 field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu);
2830 }
2831 field_ptr_info.packed_offset = packed_offset: {
2832 comptime assert(Type.packed_struct_layout_version == 2);
2833 const bit_offset = zcu.structPackedFieldBitOffset(
2834 ip.loadStructType(aggregate_ty.toIntern()),
2835 field_index,
2836 );
2837 break :packed_offset if (ptr_info.packed_offset.host_size != 0) .{
2838 .host_size = ptr_info.packed_offset.host_size,
2839 .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset,
2840 } else .{
2841 .host_size = switch (zcu.comp.getZigBackend()) {
2842 else => @intCast((aggregate_ty.bitSize(zcu) + 7) / 8),
2843 .stage2_x86_64, .stage2_c => @intCast(aggregate_ty.abiSize(zcu)),
2844 },
2845 .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset,
2846 };
2847 };
2848 field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern();
2849 return pt.ptrType(field_ptr_info);
2850 },
2851 },
2852 .@"union" => switch (aggregate_ty.containerLayout(zcu)) {
2853 .auto => field: {
2854 const union_obj = ip.loadUnionType(aggregate_ty.toIntern());
2855 break :field .{
2856 .fromInterned(union_obj.field_types.get(ip)[field_index]),
2857 union_obj.field_aligns.getOrNone(ip, field_index),
2858 };
2859 },
2860 .@"extern" => {
2861 // The alignment always matches that of the union pointer. If the union pointer is
2862 // default aligned (`.none`), we may need to explicitly align the result pointer.
2863 const field_ty = aggregate_ty.fieldType(field_index, zcu);
2864 var field_ptr_info = ptr_info;
2865 field_ptr_info.child = field_ty.toIntern();
2866 if (field_ptr_info.flags.alignment == .none and
2867 Alignment.compareStrict(field_ty.abiAlignment(zcu), .neq, aggregate_ty.abiAlignment(zcu)))
2868 {
2869 field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu);
2870 }
2871 return pt.ptrType(field_ptr_info);
2872 },
2873 .@"packed" => {
2874 const field_ty = aggregate_ty.fieldType(field_index, zcu);
2875 var field_ptr_info = ptr_info;
2876 if (field_ptr_info.flags.alignment == .none) {
2877 const resolved_align = aggregate_ty.abiAlignment(zcu);
2878 if (field_ty.abiAlignment(zcu) != resolved_align) {
2879 field_ptr_info.flags.alignment = resolved_align;
2880 }
2881 }
2882 field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern();
2883 return pt.ptrType(field_ptr_info);
2884 },
2885 },
2886 .pointer => field: {
2887 assert(aggregate_ty.isSlice(zcu));
2888 break :field switch (field_index) {
2889 Value.slice_ptr_index => .{ aggregate_ty.slicePtrFieldType(zcu), .none },
2890 Value.slice_len_index => .{ .usize, .none },
2891 else => unreachable,
2892 };
2893 },
2894 else => unreachable,
2895 };
2896 const field_ptr_align: Alignment = a: {
2897 if (aggregate_ty.zigTypeTag(zcu) == .@"struct" and aggregate_ty.structFieldIsComptime(field_index, zcu)) {
2898 // For `comptime` fields, just use exactly what was specified, or ABI alignment if nothing was specified.
2899 break :a field_align;
2900 }
2901 const actual_field_align = switch (field_align) {
2902 .none => switch (ip.indexToKey(aggregate_ty.toIntern())) {
2903 .tuple_type, .union_type => field_ty.abiAlignment(zcu),
2904 .struct_type => field_ty.defaultStructFieldAlignment(.auto, zcu),
2905 .ptr_type => Type.usize.abiAlignment(zcu),
2906 else => unreachable,
2907 },
2908 else => |a| a,
2909 };
2910 const actual_aggregate_align = switch (ptr_info.flags.alignment) {
2911 .none => aggregate_ty.abiAlignment(zcu),
2912 else => |a| a,
2913 };
2914 if (actual_aggregate_align.compareStrict(.lt, actual_field_align)) {
2915 // Underaligned aggregate; use that alignment.
2916 assert(ptr_info.flags.alignment != .none);
2917 break :a actual_aggregate_align;
2918 }
2919 if (field_align == .none and actual_field_align == field_ty.abiAlignment(zcu)) {
2920 // No explicit annotation on the field (nor an unusual default), and the aggregate
2921 // alignment is irrelevant to us, so return an un-annotated pointer.
2922 break :a .none;
2923 }
2924 break :a actual_field_align;
2925 };
2926 var field_ptr_info = ptr_info;
2927 field_ptr_info.flags.alignment = field_ptr_align;
2928 field_ptr_info.child = field_ty.toIntern();
2929 return pt.ptrType(field_ptr_info);
2930}
2931
28552932pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTerminatedString {
28562933 return switch (ip.indexToKey(ty.toIntern())) {
28572934 .struct_type => ip.loadStructType(ty.toIntern()).name,
src/Value.zig+73-229
......@@ -1687,9 +1687,6 @@ pub fn makeBool(x: bool) Value {
16871687/// `parent_ptr` must be a single-pointer or C pointer to some optional.
16881688///
16891689/// Returns a pointer to the payload of the optional.
1690///
1691/// May perform type resolution.
1692/// MLUGG TODO audit
16931690pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
16941691 const zcu = pt.zcu;
16951692 const parent_ptr_ty = parent_ptr.typeOf(zcu);
......@@ -1715,7 +1712,7 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
17151712 }
17161713
17171714 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, opt_ty, pt);
1718 return Value.fromInterned(try pt.intern(.{ .ptr = .{
1715 return .fromInterned(try pt.intern(.{ .ptr = .{
17191716 .ty = result_ty.toIntern(),
17201717 .base_addr = .{ .opt_payload = base_ptr.toIntern() },
17211718 .byte_offset = 0,
......@@ -1724,8 +1721,6 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
17241721
17251722/// `parent_ptr` must be a single-pointer to some error union.
17261723/// Returns a pointer to the payload of the error union.
1727/// May perform type resolution.
1728/// MLUGG TODO audit
17291724pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
17301725 const zcu = pt.zcu;
17311726 const parent_ptr_ty = parent_ptr.typeOf(zcu);
......@@ -1745,137 +1740,57 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
17451740 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
17461741
17471742 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, eu_ty, pt);
1748 return Value.fromInterned(try pt.intern(.{ .ptr = .{
1743 return .fromInterned(try pt.intern(.{ .ptr = .{
17491744 .ty = result_ty.toIntern(),
17501745 .base_addr = .{ .eu_payload = base_ptr.toIntern() },
17511746 .byte_offset = 0,
17521747 } }));
17531748}
17541749
1755// MLUGG TODO: audit ptrField etc in terms of resolution, and probably move them under sema
1756
1757/// `parent_ptr` must be a single-pointer or c pointer to a struct, union, or slice.
1750/// `parent_ptr` must be a single-item pointer or C pointer to a struct, union, or slice.
17581751///
17591752/// Returns a pointer to the aggregate field at the specified index.
17601753///
17611754/// For slices, uses `slice_ptr_index` and `slice_len_index`.
17621755///
1763/// May perform type resolution.
1756/// Asserts that the layout of the aggregate type is resolved.
17641757pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
17651758 const zcu = pt.zcu;
17661759 const parent_ptr_ty = parent_ptr.typeOf(zcu);
17671760 const aggregate_ty = parent_ptr_ty.childType(zcu);
1761 aggregate_ty.assertHasLayout(zcu);
17681762
17691763 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
17701764 assert(parent_ptr_info.flags.size == .one or parent_ptr_info.flags.size == .c);
17711765
1772 // Exiting this `switch` indicates that the `field` pointer representation should be used.
1773 const field_ty: Type, const new_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
1774 .@"struct" => field: {
1775 const field_ty = aggregate_ty.fieldType(field_idx, zcu);
1776 switch (aggregate_ty.containerLayout(zcu)) {
1777 .auto => break :field .{ field_ty, a: {
1778 if (parent_ptr_info.flags.alignment == .none) {
1779 break :a aggregate_ty.explicitFieldAlignment(field_idx, zcu);
1780 }
1781 const field_align = aggregate_ty.resolvedFieldAlignment(field_idx, zcu);
1782 break :a field_align.min(parent_ptr_info.flags.alignment);
1783 } },
1784 .@"extern" => {
1785 // Well-defined layout, so just offset the pointer appropriately.
1786 const byte_off = aggregate_ty.structFieldOffset(field_idx, zcu);
1787 const field_align: InternPool.Alignment = a: {
1788 if (byte_off == 0) break :a parent_ptr_info.flags.alignment;
1789 const true_field_align: InternPool.Alignment = .fromLog2Units(@ctz(byte_off));
1790 if (parent_ptr_info.flags.alignment == .none and
1791 true_field_align == field_ty.abiAlignment(zcu))
1792 {
1793 break :a .none;
1794 }
1795 const parent_align = if (parent_ptr_info.flags.alignment == .none) pa: {
1796 break :pa aggregate_ty.abiAlignment(zcu);
1797 } else parent_ptr_info.flags.alignment;
1798 break :a .minStrict(true_field_align, parent_align);
1799 };
1800 const result_ty = try pt.ptrType(info: {
1801 var new = parent_ptr_info;
1802 new.child = field_ty.toIntern();
1803 new.flags.alignment = field_align;
1804 break :info new;
1805 });
1806 return parent_ptr.getOffsetPtr(byte_off, result_ty, pt);
1807 },
1808 .@"packed" => {
1809 const packed_offset = aggregate_ty.packedStructFieldPtrInfo(parent_ptr_ty, field_idx, pt);
1810 const result_ty = try pt.ptrType(info: {
1811 var new = parent_ptr_info;
1812 new.packed_offset = packed_offset;
1813 new.child = field_ty.toIntern();
1814 if (new.flags.alignment == .none) {
1815 new.flags.alignment = aggregate_ty.abiAlignment(zcu);
1816 }
1817 break :info new;
1818 });
1819 return pt.getCoerced(parent_ptr, result_ty);
1820 },
1821 }
1822 },
1823 .@"union" => field: {
1824 const union_obj = zcu.typeToUnion(aggregate_ty).?;
1825 const field_ty = Type.fromInterned(union_obj.field_types.get(&zcu.intern_pool)[field_idx]);
1826 switch (aggregate_ty.containerLayout(zcu)) {
1827 .auto => break :field .{ field_ty, a: {
1828 if (parent_ptr_info.flags.alignment == .none) {
1829 break :a aggregate_ty.explicitFieldAlignment(field_idx, zcu);
1830 }
1831 const field_align = aggregate_ty.resolvedFieldAlignment(field_idx, zcu);
1832 break :a field_align.min(parent_ptr_info.flags.alignment);
1833 } },
1834 .@"extern" => {
1835 // Point to the same address.
1836 const result_ty = try pt.ptrType(info: {
1837 var new = parent_ptr_info;
1838 new.child = field_ty.toIntern();
1839 break :info new;
1840 });
1841 return pt.getCoerced(parent_ptr, result_ty);
1842 },
1843 .@"packed" => {
1844 const result_ty = try pt.ptrType(info: {
1845 var new = parent_ptr_info;
1846 new.child = field_ty.toIntern();
1847 break :info new;
1848 });
1849 return pt.getCoerced(parent_ptr, result_ty);
1850 },
1851 }
1766 const field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_idx, pt);
1767
1768 switch (aggregate_ty.zigTypeTag(zcu)) {
1769 .pointer => assert(aggregate_ty.isSlice(zcu)),
1770 .@"struct" => switch (aggregate_ty.containerLayout(zcu)) {
1771 .auto => {},
1772 .@"extern" => return parent_ptr.getOffsetPtr(
1773 aggregate_ty.structFieldOffset(field_idx, zcu),
1774 field_ptr_ty,
1775 pt,
1776 ),
1777 .@"packed" => return pt.getCoerced(parent_ptr, field_ptr_ty),
18521778 },
1853 .pointer => field_ty: {
1854 assert(aggregate_ty.isSlice(zcu));
1855 break :field_ty .{ switch (field_idx) {
1856 Value.slice_ptr_index => aggregate_ty.slicePtrFieldType(zcu),
1857 Value.slice_len_index => Type.usize,
1858 else => unreachable,
1859 }, switch (parent_ptr_info.flags.alignment) {
1860 .none => .none,
1861 else => Type.usize.abiAlignment(zcu).min(parent_ptr_info.flags.alignment),
1862 } };
1779 .@"union" => switch (aggregate_ty.containerLayout(zcu)) {
1780 .auto => {},
1781 .@"packed", .@"extern" => return pt.getCoerced(parent_ptr, field_ptr_ty),
18631782 },
18641783 else => unreachable,
1865 };
1784 }
18661785
1867 const result_ty = try pt.ptrType(info: {
1868 var new = parent_ptr_info;
1869 new.child = field_ty.toIntern();
1870 new.flags.alignment = new_align;
1871 break :info new;
1872 });
1786 // If we get here, we need to use the `.field` comptime pointer representation, because the
1787 // aggregate does not have a well-defined layout.
18731788
1874 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
1789 if (parent_ptr.isUndef(zcu)) return pt.undefValue(field_ptr_ty);
18751790
18761791 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, aggregate_ty, pt);
1877 return Value.fromInterned(try pt.intern(.{ .ptr = .{
1878 .ty = result_ty.toIntern(),
1792 return .fromInterned(try pt.intern(.{ .ptr = .{
1793 .ty = field_ptr_ty.toIntern(),
18791794 .base_addr = .{ .field = .{
18801795 .base = base_ptr.toIntern(),
18811796 .index = field_idx,
......@@ -1884,10 +1799,9 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
18841799 } }));
18851800}
18861801
1887/// `orig_parent_ptr` must be either a single-pointer to an array or vector, or a many-pointer or C-pointer or slice.
1802/// `orig_parent_ptr` must be either a single-pointer to an array, a slice, a many-item pointer, or a C pointer.
18881803/// Returns a pointer to the element at the specified index.
1889/// May perform type resolution.
1890/// MLUGG TODO AUDIT
1804/// Asserts that the layout of the pointer element type is resolved.
18911805pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value {
18921806 const zcu = pt.zcu;
18931807 const parent_ptr = switch (orig_parent_ptr.typeOf(zcu).ptrSize(zcu)) {
......@@ -1896,77 +1810,50 @@ pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value
18961810 };
18971811
18981812 const parent_ptr_ty = parent_ptr.typeOf(zcu);
1899 const elem_ty = parent_ptr_ty.childType(zcu);
1900 const result_ty = try parent_ptr_ty.elemPtrType(@intCast(field_idx), pt);
1813 const result_ty = try parent_ptr_ty.elemPtrType(field_idx, pt);
1814 const elem_ty = result_ty.childType(zcu);
1815 elem_ty.assertHasLayout(zcu);
19011816
19021817 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
19031818
1904 if (result_ty.ptrInfo(zcu).packed_offset.host_size != 0) {
1905 // Since we have a bit-pointer, the pointer address should be unchanged.
1906 assert(elem_ty.zigTypeTag(zcu) == .vector);
1907 return pt.getCoerced(parent_ptr, result_ty);
1819 if (!elem_ty.comptimeOnly(zcu)) {
1820 const byte_offset = field_idx * elem_ty.abiSize(zcu);
1821 return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt);
19081822 }
19091823
1910 const PtrStrat = union(enum) {
1911 offset: u64,
1912 elem_ptr: Type, // many-ptr elem ty
1913 };
1914
1915 const strat: PtrStrat = switch (parent_ptr_ty.ptrSize(zcu)) {
1916 .one => switch (elem_ty.zigTypeTag(zcu)) {
1917 .vector => .{ .offset = field_idx * @divExact(elem_ty.childType(zcu).bitSize(zcu), 8) },
1918 .array => strat: {
1919 const arr_elem_ty = elem_ty.childType(zcu);
1920 if (arr_elem_ty.comptimeOnly(zcu)) break :strat .{ .elem_ptr = arr_elem_ty };
1921 break :strat .{ .offset = field_idx * arr_elem_ty.abiSize(zcu) };
1922 },
1923 else => unreachable,
1924 },
1925
1926 .many, .c => if (elem_ty.comptimeOnly(zcu))
1927 .{ .elem_ptr = elem_ty }
1928 else
1929 .{ .offset = field_idx * elem_ty.abiSize(zcu) },
1824 // Comptime-only element type.
19301825
1931 .slice => unreachable,
1932 };
1826 if (field_idx == 0) {
1827 return pt.getCoerced(parent_ptr, result_ty);
1828 }
19331829
1934 switch (strat) {
1935 .offset => |byte_offset| {
1936 return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt);
1937 },
1938 .elem_ptr => |manyptr_elem_ty| if (field_idx == 0) {
1939 return pt.getCoerced(parent_ptr, result_ty);
1940 } else {
1941 const arr_base_ty, const arr_base_len = manyptr_elem_ty.arrayBase(zcu);
1942 const base_idx = arr_base_len * field_idx;
1943 const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr;
1944 switch (parent_info.base_addr) {
1945 .arr_elem => |arr_elem| {
1946 if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) {
1947 // We already have a pointer to an element of an array of this type.
1948 // Just modify the index.
1949 return Value.fromInterned(try pt.intern(.{ .ptr = ptr: {
1950 var new = parent_info;
1951 new.base_addr.arr_elem.index += base_idx;
1952 new.ty = result_ty.toIntern();
1953 break :ptr new;
1954 } }));
1955 }
1956 },
1957 else => {},
1830 const arr_base_ty, const arr_base_len = elem_ty.arrayBase(zcu);
1831 const base_idx = arr_base_len * field_idx;
1832 const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr;
1833 switch (parent_info.base_addr) {
1834 .arr_elem => |arr_elem| {
1835 if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) {
1836 // We already have a pointer to an element of an array of this type.
1837 // Just modify the index.
1838 return .fromInterned(try pt.intern(.{ .ptr = ptr: {
1839 var new = parent_info;
1840 new.base_addr.arr_elem.index += base_idx;
1841 new.ty = result_ty.toIntern();
1842 break :ptr new;
1843 } }));
19581844 }
1959 const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt);
1960 return Value.fromInterned(try pt.intern(.{ .ptr = .{
1961 .ty = result_ty.toIntern(),
1962 .base_addr = .{ .arr_elem = .{
1963 .base = base_ptr.toIntern(),
1964 .index = base_idx,
1965 } },
1966 .byte_offset = 0,
1967 } }));
19681845 },
1846 else => {},
19691847 }
1848 const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt);
1849 return .fromInterned(try pt.intern(.{ .ptr = .{
1850 .ty = result_ty.toIntern(),
1851 .base_addr = .{ .arr_elem = .{
1852 .base = base_ptr.toIntern(),
1853 .index = base_idx,
1854 } },
1855 .byte_offset = 0,
1856 } }));
19701857}
19711858
19721859fn canonicalizeBasePtr(base_ptr: Value, want_size: std.builtin.Type.Pointer.Size, want_child: Type, pt: Zcu.PerThread) !Value {
......@@ -2062,19 +1949,11 @@ pub const PointerDeriveStep = union(enum) {
20621949 }
20631950};
20641951
2065pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread) Allocator.Error!PointerDeriveStep {
2066 return ptr_val.pointerDerivationAdvanced(arena, pt, false, null) catch |err| switch (err) {
2067 error.OutOfMemory => |e| return e,
2068 error.Canceled => @panic("TODO"), // pls remove from error set mlugg
2069 error.AnalysisFail => unreachable,
2070 };
2071}
2072
20731952/// Given a pointer value, get the sequence of steps to derive it, ideally by taking
20741953/// only field and element pointers with no casts. This can be used by codegen backends
20751954/// which prefer field/elem accesses when lowering constant pointer values.
20761955/// It is also used by the Value printing logic for pointers.
2077pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, comptime resolve_types: bool, opt_sema: ?*Sema) !PointerDeriveStep {
1956pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, opt_sema: ?*Sema) Allocator.Error!PointerDeriveStep {
20781957 // MLUGG TODO: audit tf outta this code
20791958 const zcu = pt.zcu;
20801959 const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
......@@ -2118,7 +1997,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
21181997 const base_ptr = Value.fromInterned(eu_ptr);
21191998 const base_ptr_ty = base_ptr.typeOf(zcu);
21201999 const parent_step = try arena.create(PointerDeriveStep);
2121 parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(eu_ptr), arena, pt, resolve_types, opt_sema);
2000 parent_step.* = try pointerDerivation(.fromInterned(eu_ptr), arena, pt, opt_sema);
21222001 break :base .{ .eu_payload_ptr = .{
21232002 .parent = parent_step,
21242003 .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).errorUnionPayload(zcu)),
......@@ -2128,7 +2007,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
21282007 const base_ptr = Value.fromInterned(opt_ptr);
21292008 const base_ptr_ty = base_ptr.typeOf(zcu);
21302009 const parent_step = try arena.create(PointerDeriveStep);
2131 parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(opt_ptr), arena, pt, resolve_types, opt_sema);
2010 parent_step.* = try pointerDerivation(.fromInterned(opt_ptr), arena, pt, opt_sema);
21322011 break :base .{ .opt_payload_ptr = .{
21332012 .parent = parent_step,
21342013 .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).optionalChild(zcu)),
......@@ -2137,48 +2016,27 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
21372016 .field => |field| base: {
21382017 const base_ptr = Value.fromInterned(field.base);
21392018 const base_ptr_ty = base_ptr.typeOf(zcu);
2140 const agg_ty = base_ptr_ty.childType(zcu);
2141 if (resolve_types) try opt_sema.?.ensureLayoutResolved(agg_ty, .unneeded); // MLUGG TODO: unneeded is a hack
2142 const field_ty: Type, const field_align: InternPool.Alignment = switch (agg_ty.zigTypeTag(zcu)) {
2143 .@"struct", .@"union" => .{ agg_ty.fieldType(@intCast(field.index), zcu), agg_ty.resolvedFieldAlignment(@intCast(field.index), pt.zcu) },
2144 .pointer => switch (field.index) {
2145 Value.slice_ptr_index => .{ agg_ty.slicePtrFieldType(zcu), Type.ptrAbiAlignment(zcu.getTarget()) },
2146 Value.slice_len_index => .{ .usize, Type.abiAlignment(.usize, zcu) },
2147 else => unreachable,
2148 },
2149 else => unreachable,
2150 };
2151 const base_align = base_ptr_ty.ptrAlignment(zcu);
2152 const result_align = field_align.minStrict(base_align);
2153 const result_ty = try pt.ptrType(.{
2154 .child = field_ty.toIntern(),
2155 .flags = flags: {
2156 var flags = base_ptr_ty.ptrInfo(zcu).flags;
2157 if (result_align == field_ty.abiAlignment(zcu)) {
2158 flags.alignment = .none;
2159 } else {
2160 flags.alignment = result_align;
2161 }
2162 break :flags flags;
2163 },
2164 });
21652019 const parent_step = try arena.create(PointerDeriveStep);
2166 parent_step.* = try pointerDerivationAdvanced(base_ptr, arena, pt, resolve_types, opt_sema);
2020 parent_step.* = try pointerDerivation(base_ptr, arena, pt, opt_sema);
21672021 break :base .{ .field_ptr = .{
21682022 .parent = parent_step,
21692023 .field_idx = @intCast(field.index),
2170 .result_ptr_ty = result_ty,
2024 .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field.index), pt),
21712025 } };
21722026 },
21732027 .arr_elem => |arr_elem| base: {
21742028 const parent_step = try arena.create(PointerDeriveStep);
2175 parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(arr_elem.base), arena, pt, resolve_types, opt_sema);
2029 parent_step.* = try pointerDerivation(.fromInterned(arr_elem.base), arena, pt, opt_sema);
21762030 const parent_ptr_info = (try parent_step.ptrType(pt)).ptrInfo(zcu);
21772031 const result_ptr_ty = try pt.ptrType(.{
21782032 .child = parent_ptr_info.child,
21792033 .flags = flags: {
21802034 var flags = parent_ptr_info.flags;
21812035 flags.size = .one;
2036 if (flags.alignment != .none) flags.alignment = .minStrict(
2037 flags.alignment,
2038 Type.fromInterned(parent_ptr_info.child).abiAlignment(zcu),
2039 );
21822040 break :flags flags;
21832041 },
21842042 });
......@@ -2299,26 +2157,12 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
22992157 const end_off = start_off + field_ty.abiSize(zcu);
23002158 if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) {
23012159 const old_ptr_ty = try cur_derive.ptrType(pt);
2302 const parent_align = old_ptr_ty.ptrAlignment(zcu);
2303 const field_align = InternPool.Alignment.fromLog2Units(@min(parent_align.toLog2Units(), @ctz(start_off)));
23042160 const parent = try arena.create(PointerDeriveStep);
23052161 parent.* = cur_derive;
2306 const new_ptr_ty = try pt.ptrType(.{
2307 .child = field_ty.toIntern(),
2308 .flags = flags: {
2309 var flags = old_ptr_ty.ptrInfo(zcu).flags;
2310 if (field_align == field_ty.abiAlignment(zcu)) {
2311 flags.alignment = .none;
2312 } else {
2313 flags.alignment = field_align;
2314 }
2315 break :flags flags;
2316 },
2317 });
23182162 cur_derive = .{ .field_ptr = .{
23192163 .parent = parent,
23202164 .field_idx = @intCast(field_idx),
2321 .result_ptr_ty = new_ptr_ty,
2165 .result_ptr_ty = try old_ptr_ty.fieldPtrType(@intCast(field_idx), pt),
23222166 } };
23232167 cur_offset -= start_off;
23242168 break;
src/Zcu.zig+3-3
......@@ -3813,9 +3813,9 @@ pub const AtomicPtrAlignmentDiagnostics = struct {
38133813 max_bits: u16 = undefined,
38143814};
38153815
3816/// If ABI alignment of `ty` is OK for atomic operations, returns 0.
3817/// Otherwise returns the alignment required on a pointer for the target
3818/// to perform atomic operations.
3816/// Returns the alignment required for the target to perform atomic operations on type `ty` (that
3817/// is, the required align attribute on the pointer). If the ABI alignment of `ty` is sufficient,
3818/// returns `.none`.
38193819// TODO this function does not take into account CPU features, which can affect
38203820// this value. Audit this!
38213821pub fn atomicPtrAlignment(
src/Zcu/PerThread.zig+11-13
......@@ -3943,10 +3943,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err
39433943 return pt.ptrType(.{
39443944 .child = ty,
39453945 .flags = .{
3946 .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu))
3947 .none
3948 else
3949 alignment,
3946 .alignment = alignment,
39503947 .address_space = @"addrspace",
39513948 .is_const = is_const,
39523949 },
......@@ -4015,23 +4012,24 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
40154012 namespace.generation = zcu.generation;
40164013}
40174014
4018pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index {
4019 const ptr_ty = (try pt.ptrType(.{
4020 .child = pt.zcu.intern_pool.typeOf(val),
4015pub fn uavValue(pt: Zcu.PerThread, val: Value) Zcu.SemaError!Value {
4016 const zcu = pt.zcu;
4017 const ptr_ty = try pt.ptrType(.{
4018 .child = val.typeOf(zcu).toIntern(),
40214019 .flags = .{
40224020 .alignment = .none,
40234021 .is_const = true,
40244022 .address_space = .generic,
40254023 },
4026 })).toIntern();
4027 return pt.intern(.{ .ptr = .{
4028 .ty = ptr_ty,
4024 });
4025 return .fromInterned(try pt.intern(.{ .ptr = .{
4026 .ty = ptr_ty.toIntern(),
40294027 .base_addr = .{ .uav = .{
4030 .val = val,
4031 .orig_ty = ptr_ty,
4028 .val = val.toIntern(),
4029 .orig_ty = ptr_ty.toIntern(),
40324030 } },
40334031 .byte_offset = 0,
4034 } });
4032 } }));
40354033}
40364034
40374035pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void {
src/codegen/c.zig+1-1
......@@ -1215,7 +1215,7 @@ pub const DeclGen = struct {
12151215 .ptr => {
12161216 var arena = std.heap.ArenaAllocator.init(zcu.gpa);
12171217 defer arena.deinit();
1218 const derivation = try val.pointerDerivation(arena.allocator(), pt);
1218 const derivation = try val.pointerDerivation(arena.allocator(), pt, null);
12191219 try dg.renderPointer(w, derivation, location);
12201220 },
12211221 .opt => |opt| switch (ctype.info(ctype_pool)) {
src/codegen/spirv/CodeGen.zig+1-1
......@@ -1038,7 +1038,7 @@ fn constantPtr(cg: *CodeGen, ptr_val: Value) !Id {
10381038 var arena = std.heap.ArenaAllocator.init(gpa);
10391039 defer arena.deinit();
10401040
1041 const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt);
1041 const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, null);
10421042 return cg.derivePtr(derivation);
10431043}
10441044
src/print_value.zig+6-14
......@@ -25,10 +25,7 @@ pub fn formatSema(ctx: FormatContext, writer: *Writer) Writer.Error!void {
2525 const sema = ctx.opt_sema.?;
2626 return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
2727 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
28 error.ComptimeBreak, error.ComptimeReturn => unreachable,
29 error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully
30 error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
31 else => |e| return e,
28 error.WriteFailed => |e| return e,
3229 };
3330}
3431
......@@ -36,9 +33,7 @@ pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void {
3633 std.debug.assert(ctx.opt_sema == null);
3734 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
3835 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
39 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,
40 error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
41 else => |e| return e,
36 error.WriteFailed => |e| return e,
4237 };
4338}
4439
......@@ -48,7 +43,7 @@ pub fn print(
4843 level: u8,
4944 pt: Zcu.PerThread,
5045 opt_sema: ?*Sema,
51) (Writer.Error || Zcu.CompileError)!void {
46) (Writer.Error || Allocator.Error)!void {
5247 const zcu = pt.zcu;
5348 const ip = &zcu.intern_pool;
5449 switch (ip.indexToKey(val.toIntern())) {
......@@ -212,7 +207,7 @@ fn printAggregate(
212207 level: u8,
213208 pt: Zcu.PerThread,
214209 opt_sema: ?*Sema,
215) (Writer.Error || Zcu.CompileError)!void {
210) (Writer.Error || Allocator.Error)!void {
216211 if (level == 0) {
217212 if (is_ref) try writer.writeByte('&');
218213 return writer.writeAll(".{ ... }");
......@@ -307,7 +302,7 @@ fn printPtr(
307302 level: u8,
308303 pt: Zcu.PerThread,
309304 opt_sema: ?*Sema,
310) (Writer.Error || Zcu.CompileError)!void {
305) (Writer.Error || Allocator.Error)!void {
311306 const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
312307 .undef => return writer.writeAll("undefined"),
313308 .ptr => |ptr| ptr,
......@@ -332,10 +327,7 @@ fn printPtr(
332327
333328 var arena = std.heap.ArenaAllocator.init(pt.zcu.gpa);
334329 defer arena.deinit();
335 const derivation = if (opt_sema) |sema|
336 try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, true, sema)
337 else
338 try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null);
330 const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, opt_sema);
339331
340332 _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{
341333 .level = level,