commit b8997f871fc63cee28d94168330d84f177543f2c (tree)
parent 38fdced8bb255d3460afefaacce9dc89dab97def
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date: Sat, 31 Jan 2026 18:14:31 +0000
Sema: clean up and fix alignment handling
Diffstat:
8 files changed, 743 insertions(+), 901 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -3595,7 +3595,20 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
};
break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
},
- .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(),
+ .elem => |idx| ptr: {
+ const parent_ptr_val: Value = .fromInterned(decl_parent_ptr);
+ if (parent_ptr_val.typeOf(zcu).childType(zcu).zigTypeTag(zcu) == .vector) {
+ const elem_ptr_ty: Type = .fromInterned(new_ptr_ty);
+ // Vectors are a bit weird; see logic in `elemPtrVector`.
+ if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
+ break :ptr (try pt.getCoerced(parent_ptr_val, elem_ptr_ty)).toIntern();
+ } else {
+ const bit_offset = idx * @divExact(elem_ptr_ty.childType(zcu).bitSize(zcu), 8);
+ break :ptr (try parent_ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt)).toIntern();
+ }
+ }
+ break :ptr (try parent_ptr_val.ptrElem(idx, pt)).toIntern();
+ },
};
try ptr_mapping.put(air_ptr, new_ptr);
}
@@ -4540,10 +4553,7 @@ fn validateStructInit(
};
const field_src = init_src; // TODO better source location
- const default_field_ptr = if (struct_ty.isTuple(zcu))
- try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
- else
- try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
+ const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
}
@@ -4959,11 +4969,11 @@ pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!
.ty = array_ty.toIntern(),
.storage = .{ .bytes = string },
} });
- return sema.uavRef(val);
+ return sema.uavRef(.fromInterned(val));
}
-fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref {
- return Air.internedToRef(try sema.pt.refValue(val));
+fn uavRef(sema: *Sema, val: Value) CompileError!Air.Inst.Ref {
+ return .fromValue(try sema.pt.uavValue(val));
}
fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -6226,7 +6236,7 @@ fn popErrorReturnTrace(
const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
- const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
+ const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty);
try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
} else if (is_non_error == null) {
// 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(
const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
- const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
+ const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty);
try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
_ = try then_block.addBr(cond_block_inst, .void_value);
@@ -8196,23 +8206,10 @@ fn zirOptionalPayload(
const operand_ty = sema.typeOf(operand);
const result_ty = switch (operand_ty.zigTypeTag(zcu)) {
.optional => operand_ty.optionalChild(zcu),
- .pointer => t: {
- if (operand_ty.ptrSize(zcu) != .c) {
- return sema.failWithExpectedOptionalType(block, src, operand_ty);
- }
- // TODO https://github.com/ziglang/zig/issues/6597
- if (true) break :t operand_ty;
- const ptr_info = operand_ty.ptrInfo(zcu);
- break :t try pt.ptrType(.{
- .child = ptr_info.child,
- .flags = .{
- .alignment = ptr_info.flags.alignment,
- .is_const = ptr_info.flags.is_const,
- .is_volatile = ptr_info.flags.is_volatile,
- .is_allowzero = ptr_info.flags.is_allowzero,
- .address_space = ptr_info.flags.address_space,
- },
- });
+ // TODO: https://github.com/ziglang/zig/issues/6597 will eliminate this branch so that we only need to handle optionals.
+ .pointer => switch (operand_ty.ptrSize(zcu)) {
+ .c => operand_ty, // if `ptr` is a `[*c]T`, then `ptr.?` is also a `[*c]T`
+ .one, .many, .slice => return sema.failWithExpectedOptionalType(block, src, operand_ty),
},
else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
};
@@ -10322,10 +10319,10 @@ fn analyzeSwitchBlock(
const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
const payload_ref: Air.Inst.Ref = payload_ref: {
- const item_val: InternPool.Index = switch (operand_ty.zigTypeTag(zcu)) {
+ const item_val: Value = switch (operand_ty.zigTypeTag(zcu)) {
.@"union" => item_val: {
if (maybe_operand_opv) |operand_opv| {
- break :item_val zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val;
+ break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val);
}
assert(union_originally); // operand type must be union, otherwise it would be an OPV type here
assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture
@@ -10362,10 +10359,10 @@ fn analyzeSwitchBlock(
validated_switch.else_err_ty,
);
},
- else => item_opv.toIntern(),
+ else => item_opv,
};
break :payload_ref switch (capture) {
- .by_val => .fromIntern(item_val),
+ .by_val => .fromValue(item_val),
.by_ref => try sema.uavRef(item_val),
.none => unreachable,
};
@@ -12198,7 +12195,7 @@ fn analyzeSwitchPayloadCapture(
return case_block.addStructFieldVal(operand_val, field_index, field_ty);
}
} else if (capture_by_ref) {
- return sema.uavRef(item_val.toIntern());
+ return sema.uavRef(item_val);
} else {
return kind.inline_ref;
}
@@ -12280,37 +12277,14 @@ fn analyzeSwitchPayloadCapture(
// By-reference captures have some further restrictions which make them easier to emit
if (capture_by_ref) {
- const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu);
+ const operand_ptr_ty = sema.typeOf(operand_ptr);
const capture_ptr_ty = resolve: {
// By-ref captures of hetereogeneous types are only allowed if all field
// pointer types are peer resolvable to each other.
// We need values to run PTR on, so make a bunch of undef constants.
const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
- for (field_indices, dummy_captures) |field_idx, *dummy| {
- const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
- const field_ptr_ty = try pt.ptrType(.{
- .child = field_ty.toIntern(),
- .flags = .{
- .is_const = operand_ptr_info.flags.is_const,
- .is_volatile = operand_ptr_info.flags.is_volatile,
- .address_space = operand_ptr_info.flags.address_space,
- // TODO MLUGG: double-check this. and, um, EVERYWHERE we do ptr alignment...
- .alignment = a: {
- if (operand_ty.explicitFieldAlignment(field_idx, zcu) == .none and
- operand_ptr_info.flags.alignment == .none)
- {
- break :a .none;
- }
-
- const union_align = switch (operand_ptr_info.flags.alignment) {
- .none => operand_ty.abiAlignment(zcu),
- else => |a| a,
- };
- const field_align = operand_ty.resolvedFieldAlignment(field_idx, zcu);
- break :a .minStrict(union_align, field_align);
- },
- },
- });
+ for (field_indices, dummy_captures) |field_index, *dummy| {
+ const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt);
dummy.* = try pt.undefRef(field_ptr_ty);
}
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
element_vals[elem_i] = coerced_elem_val.toIntern();
}
return sema.addConstantMaybeRef(
- (try pt.aggregateValue(result_ty, element_vals)).toIntern(),
+ try pt.aggregateValue(result_ty, element_vals),
ptr_addrspace != null,
);
} else break :rs rhs_src;
@@ -14094,7 +14068,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
}
break :v try pt.aggregateValue(result_ty, element_vals);
};
- return sema.addConstantMaybeRef(val.toIntern(), ptr_addrspace != null);
+ return sema.addConstantMaybeRef(val, ptr_addrspace != null);
}
try sema.requireRuntimeBlock(block, src, lhs_src);
@@ -15270,7 +15244,7 @@ fn analyzeArithmetic(
};
try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src);
- return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
+ return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, rhs_src);
},
}
}
@@ -15370,7 +15344,6 @@ fn analyzePtrArithmetic(
ptr: Air.Inst.Ref,
uncasted_offset: Air.Inst.Ref,
air_tag: Air.Inst.Tag,
- ptr_src: LazySrcLoc,
offset_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
// TODO if the operand is comptime-known to be negative, or is a negative int,
@@ -15378,12 +15351,14 @@ fn analyzePtrArithmetic(
const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src);
const pt = sema.pt;
const zcu = pt.zcu;
- const opt_ptr_val = sema.resolveValue(ptr);
- const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
const ptr_ty = sema.typeOf(ptr);
const ptr_info = ptr_ty.ptrInfo(zcu);
assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c);
+ const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, offset_src, offset)) |val| off: {
+ break :off val.toUnsignedInt(zcu);
+ } else null;
+
const elem_ty: Type = .fromInterned(ptr_info.child);
elem_ty.assertHasLayout(zcu);
@@ -15395,70 +15370,36 @@ fn analyzePtrArithmetic(
else => {},
}
- const new_ptr_ty = t: {
- // Calculate the new pointer alignment.
- // This code is duplicated in `Type.elemPtrType`.
- if (ptr_info.flags.alignment == .none) {
- // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
- break :t ptr_ty;
- }
- // If the addend is not a comptime-known value we can still count on
- // it being a multiple of the type size.
- const elem_size = elem_ty.abiSize(zcu);
- const addend = if (opt_off_val) |off_val| a: {
- const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(zcu));
- break :a elem_size * off_int;
- } else elem_size;
-
- // The resulting pointer is aligned to the lcd between the offset (an
- // arbitrary number) and the alignment factor (always a power of two,
- // non zero).
- const new_align: Alignment = @enumFromInt(@min(
- @ctz(addend),
- @intFromEnum(ptr_info.flags.alignment),
- ));
- assert(new_align != .none);
-
- break :t try pt.ptrType(.{
- .child = ptr_info.child,
- .sentinel = ptr_info.sentinel,
- .flags = .{
- .size = ptr_info.flags.size,
- .alignment = new_align,
- .is_const = ptr_info.flags.is_const,
- .is_volatile = ptr_info.flags.is_volatile,
- .is_allowzero = ptr_info.flags.is_allowzero,
- .address_space = ptr_info.flags.address_space,
- },
- });
- };
+ const elem_ptr_ty = try ptr_ty.elemPtrType(maybe_index, pt);
+ // `elem_ptr_ty` is a single-item pointer, but we want a many-item or C pointer, and to preserve
+ // any input sentinel.
+ const new_ptr_ty = try pt.ptrType(info: {
+ var info = elem_ptr_ty.ptrInfo(zcu);
+ info.flags.size = ptr_info.flags.size;
+ info.sentinel = ptr_info.sentinel;
+ break :info info;
+ });
- const runtime_src = rs: {
- if (opt_ptr_val) |ptr_val| {
- if (opt_off_val) |offset_val| {
- if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
-
- const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(zcu));
- if (offset_int == 0) return ptr;
- if (air_tag == .ptr_sub) {
- const elem_size = elem_ty.abiSize(zcu);
- const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty);
- return Air.internedToRef(new_ptr_val.toIntern());
- } else {
- const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty);
- return Air.internedToRef(new_ptr_val.toIntern());
- }
- } else break :rs offset_src;
- } else break :rs ptr_src;
- };
+ ct: {
+ const ptr_val = sema.resolveValue(ptr) orelse break :ct;
+ if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
+ const index = maybe_index orelse break :ct;
+
+ if (index == 0) return ptr;
+ if (air_tag == .ptr_sub) {
+ const elem_size = elem_ty.abiSize(zcu);
+ return .fromValue(try sema.ptrSubtract(block, op_src, ptr_val, index * elem_size, new_ptr_ty));
+ } else {
+ return .fromValue(try pt.getCoerced(try ptr_val.ptrElem(index, pt), new_ptr_ty));
+ }
+ }
- try sema.requireRuntimeBlock(block, op_src, runtime_src);
try sema.checkLogicalPtrOperation(block, op_src, ptr_ty);
return block.addInst(.{
.tag = air_tag,
.data = .{ .ty_pl = .{
- .ty = Air.internedToRef(new_ptr_ty.toIntern()),
+ .ty = .fromType(new_ptr_ty),
.payload = try sema.addExtra(Air.Bin{
.lhs = ptr,
.rhs = offset,
@@ -16222,6 +16163,11 @@ fn zirBuiltinSrc(
return Air.internedToRef((try pt.aggregateValue(src_loc_ty, &fields)).toIntern());
}
+/// MLUGG TODO: once this branch is in a more stable state, I need to make a language change so that
+/// `std.builtin.Type` makes all `alignment` fields `?usize` instead of `comptime_int`, to prevent
+/// explicit alignment annotations from sneaking in without the user requesting any; but doing that
+/// right now would be really annoying because it would break the base compiler. I need to have the
+/// compiler more-or-less fully migrated first.
fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
@@ -16726,17 +16672,21 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
} });
};
+ const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
+
const alignment = switch (layout) {
- .auto, .@"extern" => ty.resolvedFieldAlignment(field_index, zcu),
+ .auto, .@"extern" => switch (ty.explicitFieldAlignment(field_index, zcu)) {
+ .none => field_ty.abiAlignment(zcu),
+ else => |a| a,
+ },
.@"packed" => .none,
};
- const field_ty = union_obj.field_types.get(ip)[field_index];
const union_field_fields = .{
// name: [:0]const u8,
name_val,
// type: type,
- field_ty,
+ field_ty.toIntern(),
// alignment: comptime_int,
(try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(),
};
@@ -16895,7 +16845,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const opt_default_val: ?Value = if (field_default == .none) null else .fromInterned(field_default);
const default_val_ptr = try sema.optRefValue(opt_default_val);
const alignment = switch (struct_type.layout) {
- .auto, .@"extern" => ty.resolvedFieldAlignment(field_index, zcu),
+ .auto, .@"extern" => switch (ty.explicitFieldAlignment(field_index, zcu)) {
+ .none => field_ty.defaultStructFieldAlignment(struct_type.layout, zcu),
+ else => |a| a,
+ },
.@"packed" => .none,
};
@@ -18425,8 +18378,7 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
const init_ref = try sema.coerce(block, init_ty, empty_ref, src);
if (is_byref) {
- const init_val = sema.resolveValue(init_ref).?;
- return sema.uavRef(init_val.toIntern());
+ return sema.uavRef(sema.resolveValue(init_ref).?);
} else {
return init_ref;
}
@@ -18648,7 +18600,7 @@ fn zirStructInit(
}));
const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src);
const final_val = sema.resolveValue(final_val_inst).?;
- return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
+ return sema.addConstantMaybeRef(final_val, is_ref);
}
if (resolved_ty.comptimeOnly(zcu)) {
@@ -18789,7 +18741,7 @@ fn finishStructInit(
}
const struct_val = try pt.aggregateValue(struct_ty, elems);
const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
- return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
+ return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
},
.@"packed" => {
const buf = try sema.arena.alloc(u8, (struct_ty.bitSize(zcu) + 7) / 8);
@@ -18808,7 +18760,7 @@ fn finishStructInit(
error.OutOfMemory => |e| return e,
};
const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
- return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
+ return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
},
};
@@ -19000,7 +18952,7 @@ fn structInitAnon(
_ = opt_runtime_index orelse {
const struct_val = try pt.aggregateValue(struct_ty, values);
- return sema.addConstantMaybeRef(struct_val.toIntern(), is_ref);
+ return sema.addConstantMaybeRef(struct_val, is_ref);
};
if (is_ref) {
@@ -19137,7 +19089,7 @@ fn zirArrayInit(
const arr_val = try pt.aggregateValue(array_ty, elem_vals);
const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src);
const result_val = (sema.resolveValue(result_ref)).?;
- return sema.addConstantMaybeRef(result_val.toIntern(), is_ref);
+ return sema.addConstantMaybeRef(result_val, is_ref);
};
if (is_ref) {
@@ -19261,7 +19213,7 @@ fn arrayInitAnon(
const runtime_src = opt_runtime_src orelse {
const tuple_val = try pt.aggregateValue(tuple_ty, values);
- return sema.addConstantMaybeRef(tuple_val.toIntern(), is_ref);
+ return sema.addConstantMaybeRef(tuple_val, is_ref);
};
try sema.requireRuntimeBlock(block, src, runtime_src);
@@ -19304,8 +19256,8 @@ fn arrayInitAnon(
return block.addAggregateInit(tuple_ty, element_refs);
}
-fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref {
- return if (is_ref) sema.uavRef(val) else Air.internedToRef(val);
+fn addConstantMaybeRef(sema: *Sema, val: Value, is_ref: bool) !Air.Inst.Ref {
+ return if (is_ref) sema.uavRef(val) else .fromValue(val);
}
fn 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
const field_ptr = sema.resolveInst(extra.field_ptr);
const field_ptr_ty = sema.typeOf(field_ptr);
try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty);
- const field_ptr_info = field_ptr_ty.ptrInfo(zcu);
- var actual_parent_ptr_info: InternPool.Key.PtrType = .{
- .child = parent_ty.toIntern(),
- .flags = .{
- .alignment = parent_ptr_ty.ptrAlignment(zcu),
- .is_const = field_ptr_info.flags.is_const,
- .is_volatile = field_ptr_info.flags.is_volatile,
- .is_allowzero = field_ptr_info.flags.is_allowzero,
- .address_space = field_ptr_info.flags.address_space,
- },
- .packed_offset = parent_ptr_info.packed_offset,
- };
- const field_ty = parent_ty.fieldType(field_index, zcu);
- var actual_field_ptr_info: InternPool.Key.PtrType = .{
- .child = field_ty.toIntern(),
- .flags = .{
- .alignment = field_ptr_ty.ptrAlignment(zcu),
- .is_const = field_ptr_info.flags.is_const,
- .is_volatile = field_ptr_info.flags.is_volatile,
- .is_allowzero = field_ptr_info.flags.is_allowzero,
- .address_space = field_ptr_info.flags.address_space,
- },
- .packed_offset = field_ptr_info.packed_offset,
- };
- switch (parent_ty.containerLayout(zcu)) {
- .auto => {
- actual_parent_ptr_info.flags.alignment = parent_ty.resolvedFieldAlignment(field_index, zcu);
- actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
- actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
- },
- .@"extern" => {
- const field_offset = parent_ty.structFieldOffset(field_index, zcu);
- actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0)
- Alignment.fromLog2Units(@ctz(field_offset))
- else
- actual_field_ptr_info.flags.alignment);
-
- actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
- actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
- },
- .@"packed" => {
- const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
- (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) -
- actual_field_ptr_info.packed_offset.bit_offset), 8) catch
- return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{});
- actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0)
- Alignment.fromLog2Units(@ctz(byte_offset))
- else
- actual_field_ptr_info.flags.alignment);
- },
- }
+ const hypothetical_field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_index, pt);
+ const casted_field_ptr = try sema.ptrCastFull(
+ block,
+ flags,
+ inst_src,
+ field_ptr,
+ field_ptr_src,
+ hypothetical_field_ptr_ty,
+ "@fieldParentPtr",
+ );
- const actual_field_ptr_ty = try pt.ptrType(actual_field_ptr_info);
- const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src);
- const actual_parent_ptr_ty = try pt.ptrType(actual_parent_ptr_info);
+ const unaligned_parent_ptr_ty = try pt.ptrType(info: {
+ var info = parent_ptr_ty.ptrInfo(zcu);
+ info.flags.alignment = hypothetical_field_ptr_ty.ptrAlignment(zcu);
+ break :info info;
+ });
- const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: {
- switch (parent_ty.zigTypeTag(zcu)) {
- .@"struct" => switch (parent_ty.containerLayout(zcu)) {
- .auto => {},
- .@"extern" => {
- const byte_offset = parent_ty.structFieldOffset(field_index, zcu);
- const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
- break :result Air.internedToRef(parent_ptr_val.toIntern());
- },
- .@"packed" => {
- // Logic lifted from type computation above - I'm just assuming it's correct.
- // `catch unreachable` since error case handled above.
- const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
- zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) -
- actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable;
- const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
- break :result Air.internedToRef(parent_ptr_val.toIntern());
- },
- },
- .@"union" => switch (parent_ty.containerLayout(zcu)) {
- .auto => {},
- .@"extern", .@"packed" => {
- // For an extern or packed union, just coerce the pointer.
- const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty);
- break :result Air.internedToRef(parent_ptr_val.toIntern());
- },
- },
+ const unaligned_parent_ptr: Air.Inst.Ref = if (try sema.resolveDefinedValue(
+ block,
+ field_ptr_src,
+ casted_field_ptr,
+ )) |field_ptr_val| switch (parent_ty.containerLayout(zcu)) {
+ .@"packed" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)),
+ .@"extern" => switch (parent_ty.zigTypeTag(zcu)) {
+ .@"struct" => .fromValue(try sema.ptrSubtract(
+ block,
+ field_ptr_src,
+ field_ptr_val,
+ parent_ty.structFieldOffset(field_index, zcu),
+ unaligned_parent_ptr_ty,
+ )),
+ .@"union" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)),
else => unreachable,
- }
-
- const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
- const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
- .ptr => |ptr| ptr,
- else => break :opt_field null,
- };
- if (ptr.byte_offset != 0) break :opt_field null;
- break :opt_field switch (ptr.base_addr) {
- .field => |field| field,
- else => null,
+ },
+ .auto => result: {
+ const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
+ const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
+ .ptr => |ptr| ptr,
+ else => break :opt_field null,
+ };
+ if (ptr.byte_offset != 0) break :opt_field null;
+ break :opt_field switch (ptr.base_addr) {
+ .field => |field| field,
+ else => null,
+ };
};
- };
- const field = opt_field orelse {
- return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
- };
+ const field = opt_field orelse {
+ return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
+ };
- if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
- return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
- }
+ if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
+ return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
+ }
- if (field.index != field_index) {
- return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
- field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
- });
- }
- break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src);
+ if (field.index != field_index) {
+ return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
+ field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
+ });
+ }
+ break :result .fromValue(try pt.getCoerced(.fromInterned(field.base), unaligned_parent_ptr_ty));
+ },
} else result: {
- try sema.requireRuntimeBlock(block, inst_src, field_ptr_src);
break :result try block.addInst(.{
.tag = .field_parent_ptr,
.data = .{ .ty_pl = .{
- .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()),
+ .ty = .fromType(unaligned_parent_ptr_ty),
.payload = try block.sema.addExtra(Air.FieldParentPtr{
.field_ptr = casted_field_ptr,
.field_index = @intCast(field_index),
@@ -23527,14 +23428,61 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
} },
});
};
- return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr");
+
+ // There's one more error condition: if the hypothetical field pointer type has a lower
+ // alignment than the parent pointer type, then we need an `@alignCast`. Note that the earlier
+ // `ptrCastFull` may *also* have "used" the `@alignCast`; that would be a case where the field
+ // is naturally less aligned than the rest of the struct, *and* the field pointer is itself
+ // underaligned compared to the field alignment. For example, `struct { a: u32, b: u16 }` with
+ // a field pointer of type `*align(1) u16`.
+ switch (hypothetical_field_ptr_ty.ptrAlignment(zcu).order(parent_ptr_ty.ptrAlignment(zcu))) {
+ .gt => unreachable, // getting a field pointer can never increase alignment
+ .eq => return unaligned_parent_ptr,
+ .lt => if (flags.align_cast) {
+ // Go through `ptrCastFull` for the safety check.
+ return sema.ptrCastFull(
+ block,
+ flags,
+ inst_src,
+ unaligned_parent_ptr,
+ inst_src,
+ parent_ptr_ty,
+ "@fieldParentPtr",
+ );
+ } else return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(inst_src, "@fieldParentPtr increases pointer alignment", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(inst_src, msg, "parent pointer type '{f}' has alignment '{d}'", .{
+ parent_ptr_ty.fmt(pt),
+ parent_ptr_ty.abiAlignment(zcu),
+ });
+ if (parent_ty.isTuple(zcu)) {
+ try sema.errNote(field_ptr_src, msg, "tuple field '{d}' limits alignment to '{d}'", .{
+ field_index,
+ field_ptr_ty.ptrAlignment(zcu),
+ });
+ } else {
+ try sema.errNote(parent_ty.srcLoc(zcu), msg, "{t} field '{f}' limits alignment to '{d}'", .{
+ parent_ty.zigTypeTag(zcu),
+ switch (parent_ty.zigTypeTag(zcu)) {
+ .@"struct" => parent_ty.structFieldName(field_index, zcu).unwrap().?.fmt(ip),
+ .@"union" => parent_ty.unionTagTypeHypothetical(zcu).enumFieldName(field_index, zcu).fmt(ip),
+ else => unreachable,
+ },
+ field_ptr_ty.ptrAlignment(zcu),
+ });
+ }
+ try sema.errNote(inst_src, msg, "use @alignCast to assert pointer alignment", .{});
+ break :msg msg;
+ }),
+ }
}
fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value {
const pt = sema.pt;
const zcu = pt.zcu;
if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty);
- var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
+ const ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
.undef => return sema.failWithUseOfUndef(block, src, null),
.ptr => |ptr| ptr,
else => unreachable,
@@ -23547,9 +23495,11 @@ fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte
break :msg msg;
});
}
- ptr.byte_offset -= byte_subtract;
- ptr.ty = new_ty.toIntern();
- return Value.fromInterned(try pt.intern(.{ .ptr = ptr }));
+ return Value.fromInterned(try pt.intern(.{ .ptr = .{
+ .ty = new_ty.toIntern(),
+ .base_addr = ptr.base_addr,
+ .byte_offset = ptr.byte_offset - byte_subtract,
+ } }));
}
fn zirMinMax(
@@ -24186,8 +24136,8 @@ fn zirMemcpy(
// ok1: dest >= src + len
// ok2: src >= dest + len
- const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
- const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
+ const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src);
+ const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, src);
const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
const ok = try block.addBinOp(.bool_or, ok1, ok2);
@@ -25402,21 +25352,36 @@ fn addSafetyCheckSentinelMismatch(
const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern());
const ptr_ty = sema.typeOf(ptr);
- const actual_sentinel = if (ptr_ty.isSlice(zcu))
- try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
- else blk: {
- const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt);
- const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
- break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
- };
-
- const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: {
- const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
- break :ok try parent_block.addReduce(eql, .And);
- } else ok: {
- assert(sentinel_ty.isSelfComparable(zcu, true));
- break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
+ const ptr_info = ptr_ty.ptrInfo(zcu);
+ const actual_sentinel: Air.Inst.Ref = switch (ptr_ty.ptrSize(zcu)) {
+ .slice => try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index),
+ .one => s: {
+ const array_ty: Type = .fromInterned(ptr_info.child);
+ assert(array_ty.zigTypeTag(zcu) == .array);
+ assert(array_ty.childType(zcu).toIntern() == sentinel_ty.toIntern());
+ const many_ptr_ty = try pt.ptrType(.{
+ .child = sentinel_ty.toIntern(),
+ .flags = .{
+ .size = .many,
+ .is_const = ptr_info.flags.is_const,
+ .is_volatile = ptr_info.flags.is_volatile,
+ .is_allowzero = ptr_info.flags.is_allowzero,
+ .alignment = switch (ptr_info.flags.alignment) {
+ .none => .none,
+ else => |ptr_align| .minStrict(ptr_align, sentinel_ty.abiAlignment(zcu)),
+ },
+ .address_space = ptr_info.flags.address_space,
+ },
+ });
+ const many_ptr = try parent_block.addBitCast(many_ptr_ty, ptr);
+ break :s try parent_block.addBinOp(.ptr_elem_val, many_ptr, sentinel_index);
+ },
+ .many => unreachable,
+ .c => unreachable,
};
+ assert(sema.typeOf(actual_sentinel).toIntern() == sentinel_ty.toIntern());
+ assert(sentinel_ty.isSelfComparable(zcu, true));
+ const ok = try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{
expected_sentinel, actual_sentinel,
@@ -25676,7 +25641,7 @@ fn fieldVal(
.@"struct" => if (is_pointer_to) {
// Avoid loading the entire struct by fetching a pointer and loading that
try sema.ensureLayoutResolved(inner_ty, src);
- const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
+ const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
return sema.analyzeLoad(block, src, field_ptr, object_src);
} else {
return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty);
@@ -25730,7 +25695,7 @@ fn fieldPtr(
.array => {
if (field_name.eqlSlice("len", ip)) {
const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu));
- return uavRef(sema, int_val.toIntern());
+ return uavRef(sema, int_val);
} else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
const ptr_info = object_ty.ptrInfo(zcu);
const new_ptr_ty = try pt.ptrType(.{
@@ -25752,6 +25717,7 @@ fn fieldPtr(
.child = new_ptr_ty.toIntern(),
.sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
.flags = .{
+ .size = .one,
.alignment = ptr_ptr_info.flags.alignment,
.is_const = ptr_ptr_info.flags.is_const,
.is_volatile = ptr_ptr_info.flags.is_volatile,
@@ -25857,10 +25823,10 @@ fn fieldPtr(
},
else => unreachable,
};
- return uavRef(sema, try pt.intern(.{ .err = .{
+ return uavRef(sema, .fromInterned(try pt.intern(.{ .err = .{
.ty = err_set_ty.toIntern(),
.name = field_name,
- } }));
+ } })));
},
.@"union" => {
if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
@@ -25871,7 +25837,7 @@ fn fieldPtr(
if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
const field_index_u32: u32 = @intCast(field_index);
const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32);
- return uavRef(sema, idx_val.toIntern());
+ return uavRef(sema, idx_val);
}
}
return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
@@ -25886,7 +25852,7 @@ fn fieldPtr(
};
const field_index_u32: u32 = @intCast(field_index);
const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32);
- return uavRef(sema, idx_val.toIntern());
+ return uavRef(sema, idx_val);
},
.@"struct", .@"opaque" => {
if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
@@ -25903,7 +25869,7 @@ fn fieldPtr(
else
object_ptr;
try sema.ensureLayoutResolved(inner_ty, src);
- const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
+ const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
return field_ptr;
},
@@ -26190,7 +26156,6 @@ fn structFieldPtr(
field_name: InternPool.NullTerminatedString,
field_name_src: LazySrcLoc,
struct_ty: Type,
- initializing: bool,
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
@@ -26199,23 +26164,24 @@ fn structFieldPtr(
assert(struct_ty.zigTypeTag(zcu) == .@"struct");
struct_ty.assertHasLayout(zcu);
- if (struct_ty.isTuple(zcu)) {
+ const field_index: u32 = if (struct_ty.isTuple(zcu)) field_index: {
if (field_name.eqlSlice("len", ip)) {
const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu));
return sema.analyzeRef(block, src, len_inst);
}
- const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
- return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
- }
-
- const struct_type = zcu.typeToStruct(struct_ty).?;
-
- const field_index = struct_type.nameIndex(ip, field_name) orelse
- return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
+ break :field_index try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
+ } else field_index: {
+ const struct_type = zcu.typeToStruct(struct_ty).?;
+ break :field_index struct_type.nameIndex(ip, field_name) orelse {
+ return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
+ };
+ };
return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty);
}
+/// Supports both structs and unions.
+///
/// Asserts that the layout of `struct_ty` is already resolved.
fn structFieldPtrByIndex(
sema: *Sema,
@@ -26227,82 +26193,23 @@ fn structFieldPtrByIndex(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
struct_ty.assertHasLayout(zcu);
-
- const struct_type = zcu.typeToStruct(struct_ty).?;
- const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index);
-
- // Comptime fields are handled later
- if (!field_is_comptime) {
- if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
- const val = try struct_ptr_val.ptrField(field_index, pt);
- return Air.internedToRef(val.toIntern());
- }
- }
-
- const field_ty = struct_type.field_types.get(ip)[field_index];
const struct_ptr_ty = sema.typeOf(struct_ptr);
- const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
- assert(struct_ptr_ty_info.child == struct_ty.toIntern());
-
- var ptr_ty_data: InternPool.Key.PtrType = .{
- .child = field_ty,
- .flags = .{
- .is_const = struct_ptr_ty_info.flags.is_const,
- .is_volatile = struct_ptr_ty_info.flags.is_volatile,
- .address_space = struct_ptr_ty_info.flags.address_space,
- },
- };
- const parent_align = if (struct_ptr_ty_info.flags.alignment != .none)
- struct_ptr_ty_info.flags.alignment
- else
- struct_ty.abiAlignment(zcu);
-
- if (struct_type.layout == .@"packed") {
- assert(!field_is_comptime);
- const packed_offset = struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt);
- ptr_ty_data.flags.alignment = parent_align;
- ptr_ty_data.packed_offset = packed_offset;
- } else if (struct_type.layout == .@"extern") {
- assert(!field_is_comptime);
- // For extern structs, field alignment might be bigger than type's
- // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
- // second field is aligned as u32.
- ptr_ty_data.flags.alignment = a: {
- const field_off = struct_ty.structFieldOffset(field_index, zcu);
- if (field_off == 0) break :a struct_ptr_ty_info.flags.alignment;
- const true_field_align: Alignment = .fromLog2Units(@ctz(field_off));
- if (struct_ptr_ty_info.flags.alignment == .none and
- true_field_align == Type.fromInterned(field_ty).abiAlignment(zcu))
- {
- break :a .none;
- }
- break :a .minStrict(true_field_align, parent_align);
- };
- } else {
- // Our alignment is capped at the field alignment.
- ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
- struct_ty.explicitFieldAlignment(field_index, zcu)
- else
- struct_ty.resolvedFieldAlignment(field_index, zcu).min(parent_align);
- }
-
- const ptr_field_ty = try pt.ptrType(ptr_ty_data);
-
- if (field_is_comptime) {
- assert(struct_type.field_defaults.get(ip)[field_index] != .none);
- const val = try pt.intern(.{ .ptr = .{
- .ty = ptr_field_ty.toIntern(),
- .base_addr = .{ .comptime_field = struct_type.field_defaults.get(ip)[field_index] },
+ if (struct_ty.structFieldIsComptime(field_index, zcu)) {
+ const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt);
+ return .fromIntern(try pt.intern(.{ .ptr = .{
+ .ty = field_ptr_ty.toIntern(),
+ .base_addr = .{ .comptime_field = struct_ty.structFieldDefaultValue(field_index, zcu).?.toIntern() },
.byte_offset = 0,
- } });
- return Air.internedToRef(val);
+ } }));
+ } else if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
+ return .fromValue(try struct_ptr_val.ptrField(field_index, pt));
+ } else {
+ const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt);
+ return block.addStructFieldPtr(struct_ptr, field_index, field_ptr_ty);
}
-
- return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
}
fn structFieldVal(
@@ -26438,29 +26345,11 @@ fn unionFieldPtr(
assert(union_ty.zigTypeTag(zcu) == .@"union");
union_ty.assertHasLayout(zcu);
- const union_ptr_ty = sema.typeOf(union_ptr);
- const union_ptr_info = union_ptr_ty.ptrInfo(zcu);
const union_obj = zcu.typeToUnion(union_ty).?;
+ const tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
+
const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
- const ptr_field_ty = try pt.ptrType(.{
- .child = field_ty.toIntern(),
- .flags = .{
- .is_const = union_ptr_info.flags.is_const,
- .is_volatile = union_ptr_info.flags.is_volatile,
- .address_space = union_ptr_info.flags.address_space,
- .alignment = a: {
- if (union_obj.layout != .auto) break :a union_ptr_info.flags.alignment;
- if (union_ptr_info.flags.alignment == .none) {
- break :a union_ty.explicitFieldAlignment(field_index, zcu);
- }
- const field_align = union_ty.resolvedFieldAlignment(field_index, zcu);
- break :a union_ptr_info.flags.alignment.min(field_align);
- },
- },
- .packed_offset = union_ptr_info.packed_offset,
- });
- const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_type).enumFieldIndex(field_name, zcu).?);
if (initializing and field_ty.classify(zcu) == .no_possible_value) {
const msg = msg: {
@@ -26484,23 +26373,17 @@ fn unionFieldPtr(
break :ct;
}
// Store to the union to initialize the tag.
- const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), enum_field_index);
- const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
- const new_union_val = try pt.unionValue(union_ty, field_tag, try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty));
+ const field_tag = try pt.enumValueFieldIndex(tag_ty, field_index);
+ const payload_val = try field_ty.onePossibleValue(pt) orelse try pt.undefValue(field_ty);
+ const new_union_val = try pt.unionValue(union_ty, field_tag, payload_val);
try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty);
} else {
- const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
- break :ct;
- if (union_val.isUndef(zcu)) {
- return sema.failWithUseOfUndef(block, src, null);
- }
- const un = ip.indexToKey(union_val.toIntern()).un;
- const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), enum_field_index);
- const tag_matches = un.tag == field_tag.toIntern();
- if (!tag_matches) {
+ const union_val = try sema.pointerDeref(block, src, union_ptr_val, union_ptr_val.typeOf(zcu)) orelse break :ct;
+ if (union_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
+ const active_index = tag_ty.enumTagFieldIndex(union_val.unionTag(zcu).?, zcu).?;
+ if (active_index != field_index) {
const msg = msg: {
- const active_index = Type.fromInterned(union_obj.enum_tag_type).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
- const active_field_name = Type.fromInterned(union_obj.enum_tag_type).enumFieldName(active_index, zcu);
+ const active_field_name = tag_ty.enumFieldName(active_index, zcu);
const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
field_name.fmt(ip),
active_field_name.fmt(ip),
@@ -26520,11 +26403,10 @@ fn unionFieldPtr(
// If the union has a tag, we must either set or or safety check it depending on `initializing`.
tag: {
if (union_ty.containerLayout(zcu) != .auto) break :tag;
- const tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
if (tag_ty.classify(zcu) == .one_possible_value) break :tag;
// There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
// only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
- const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
+ const want_tag = try pt.enumValueFieldIndex(tag_ty, field_index);
if (initializing) {
const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
@@ -26540,7 +26422,9 @@ fn unionFieldPtr(
_ = try block.addNoOp(.unreach);
return .unreachable_value;
}
- return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
+
+ const field_ptr_ty = try sema.typeOf(union_ptr).fieldPtrType(field_index, pt);
+ return block.addStructFieldPtr(union_ptr, field_index, field_ptr_ty);
}
fn unionFieldVal(
@@ -26628,13 +26512,9 @@ fn elemPtr(
try sema.ensureLayoutResolved(indexable_ty, src);
const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
- .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
- .@"struct" => blk: {
- // Tuple field access.
- const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
- const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
- break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
- },
+ .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
+ .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
+ .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),
else => {
const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src);
@@ -26672,21 +26552,21 @@ fn elemPtrOneLayerOnly(
.many, .c => {
const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+ const maybe_index: ?u64 = if (maybe_index_val) |val| val.toUnsignedInt(zcu) else null;
ct: {
const ptr_val = maybe_ptr_val orelse break :ct;
- const index_val = maybe_index_val orelse break :ct;
- const index: usize = @intCast(index_val.toUnsignedInt(zcu));
- const elem_ptr = try ptr_val.ptrElem(index, pt);
- return Air.internedToRef(elem_ptr.toIntern());
+ const index: usize = @intCast(maybe_index orelse break :ct);
+ return .fromValue(try ptr_val.ptrElem(index, pt));
}
try sema.checkLogicalPtrOperation(block, src, indexable_ty);
- const result_ty = try indexable_ty.elemPtrType(null, pt);
+
+ const result_ty = try indexable_ty.elemPtrType(maybe_index, pt);
try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src);
try sema.validateRuntimeValue(block, indexable_src, indexable);
- if (result_ty.childType(zcu).abiSize(zcu) == 0) {
+ if (child_ty.abiSize(zcu) == 0) {
// zero-bit child type; just bitcast the pointer
return block.addBitCast(result_ty, indexable);
}
@@ -26695,13 +26575,9 @@ fn elemPtrOneLayerOnly(
},
.one => {
const elem_ptr = switch (child_ty.zigTypeTag(zcu)) {
- .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
- .@"struct" => blk: {
- assert(child_ty.isTuple(zcu));
- const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
- const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
- break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
- },
+ .vector => try sema.elemPtrVector(block, indexable_src, indexable, elem_index_src, elem_index, init),
+ .array => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
+ .@"struct" => try sema.tupleElemPtr(block, indexable_src, indexable, elem_index, elem_index_src),
else => unreachable, // Guaranteed by checkIndexable
};
try sema.checkKnownAllocPtr(block, indexable, elem_ptr);
@@ -26746,10 +26622,8 @@ fn elemVal(
const index: usize = @intCast(index_val.toUnsignedInt(zcu));
const many_ptr_ty = try pt.manyConstPtrType(child_ty);
const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty);
- const elem_ptr_ty = try pt.singleConstPtrType(child_ty);
const elem_ptr_val = try many_ptr_val.ptrElem(index, pt);
- const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct;
- return Air.internedToRef((try pt.getCoerced(elem_val, child_ty)).toIntern());
+ return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), indexable_src);
}
if (try child_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
@@ -26824,70 +26698,38 @@ fn validateRuntimeElemAccess(
}
}
-/// Asserts that the layout of the tuple type is already resolved.
-fn tupleFieldPtr(
+/// Validates `elem_index`, and returns a pointer to that field using `structFieldPtrByIndex`.
+///
+/// Asserts that the type of `tuple_ptr` is a single-item pointer whose child type is a tuple.
+fn tupleElemPtr(
sema: *Sema,
block: *Block,
- tuple_ptr_src: LazySrcLoc,
+ src: LazySrcLoc,
tuple_ptr: Air.Inst.Ref,
- field_index_src: LazySrcLoc,
- field_index: u32,
- init: bool,
+ elem_index: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const tuple_ptr_ty = sema.typeOf(tuple_ptr);
- const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu);
- const tuple_ty: Type = .fromInterned(tuple_ptr_info.child);
- const field_count = tuple_ty.structFieldCount(zcu);
-
- tuple_ty.assertHasLayout(zcu);
+ assert(tuple_ptr_ty.isSinglePointer(zcu));
+ const tuple_ty = tuple_ptr_ty.childType(zcu);
+ assert(tuple_ty.isTuple(zcu));
+ const field_count = tuple_ty.structFieldCount(zcu);
if (field_count == 0) {
- return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
+ return sema.fail(block, src, "indexing into empty tuple is not allowed", .{});
}
- if (field_index >= field_count) {
- return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
- field_index, field_count,
+ const elem_index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
+ const index = elem_index_val.getUnsignedInt(zcu);
+ if (index == null or index.? >= field_count) {
+ return sema.fail(block, elem_index_src, "index '{f}' out of bounds of tuple '{f}'", .{
+ elem_index_val.fmtValueSema(pt, sema), tuple_ty.fmt(pt),
});
}
- const field_ty = tuple_ty.fieldType(field_index, zcu);
- const ptr_field_ty = try pt.ptrType(.{
- .child = field_ty.toIntern(),
- .flags = .{
- .is_const = tuple_ptr_info.flags.is_const,
- .is_volatile = tuple_ptr_info.flags.is_volatile,
- .address_space = tuple_ptr_info.flags.address_space,
- .alignment = a: {
- if (tuple_ptr_info.flags.alignment == .none) break :a .none;
- // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned.
- const tuple_align = tuple_ptr_info.flags.alignment;
- const field_align = field_ty.abiAlignment(zcu);
- break :a tuple_align.min(field_align);
- },
- },
- });
-
- if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| {
- return Air.internedToRef((try pt.intern(.{ .ptr = .{
- .ty = ptr_field_ty.toIntern(),
- .base_addr = .{ .comptime_field = default_val.toIntern() },
- .byte_offset = 0,
- } })));
- }
-
- if (sema.resolveValue(tuple_ptr)) |tuple_ptr_val| {
- const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt);
- return Air.internedToRef(field_ptr_val.toIntern());
- }
-
- if (!init) {
- try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
- }
-
- return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
+ return sema.structFieldPtrByIndex(block, src, tuple_ptr, @intCast(index.?), tuple_ty);
}
fn tupleField(
@@ -26994,7 +26836,102 @@ fn elemValArray(
return block.addBinOp(.array_elem_val, array, elem_index);
}
-/// Asserts that the layout of the array or vector is already resolved.
+fn elemPtrVector(
+ sema: *Sema,
+ block: *Block,
+ vector_ptr_src: LazySrcLoc,
+ vector_ptr: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
+ init: bool,
+) CompileError!Air.Inst.Ref {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const vector_ptr_ty = sema.typeOf(vector_ptr);
+ const vector_ty = vector_ptr_ty.childType(zcu);
+ assert(vector_ty.zigTypeTag(zcu) == .vector);
+ const vector_len = vector_ty.vectorLen(zcu);
+
+ if (vector_len == 0) {
+ return sema.fail(block, vector_ptr_src, "cannot index into empty vector", .{});
+ }
+
+ const maybe_vector_ptr_val = sema.resolveValue(vector_ptr);
+ // The index must not be undefined since it can be out of bounds.
+ const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse {
+ return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
+ };
+ const index = index_val.toUnsignedInt(zcu);
+ if (index >= vector_len) {
+ return sema.fail(block, elem_index_src, "index {d} outside vector of length {d}", .{ index, vector_len });
+ }
+
+ const elem_ty = vector_ty.childType(zcu);
+ const elem_bits = elem_ty.bitSize(zcu);
+ // Exiting this block means the operation is a runtime one.
+ const elem_ptr_ty: Type = if (elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits)) elem_ptr_ty: {
+ // Use a packed pointer (i.e. vector_index != 0)
+ const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu);
+ const elem_ptr_ty = try pt.ptrType(.{
+ .child = elem_ty.toIntern(),
+ .flags = .{
+ .size = .one,
+ .alignment = vector_ptr_info.flags.alignment,
+ .is_const = vector_ptr_info.flags.is_const,
+ .is_volatile = vector_ptr_info.flags.is_volatile,
+ .is_allowzero = vector_ptr_info.flags.is_allowzero,
+ .address_space = vector_ptr_info.flags.address_space,
+ .vector_index = @enumFromInt(index),
+ },
+ .packed_offset = .{
+ .host_size = @intCast(vector_len),
+ .bit_offset = 0,
+ },
+ });
+ if (maybe_vector_ptr_val) |ptr_val| {
+ if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty);
+ return .fromValue(try pt.getCoerced(ptr_val, elem_ptr_ty));
+ }
+ break :elem_ptr_ty elem_ptr_ty;
+ } else elem_ptr_ty: {
+ // Use a normal pointer (i.e. vector_index == 0)
+ const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu);
+ const elem_ptr_ty = try pt.ptrType(.{
+ .child = elem_ty.toIntern(),
+ .flags = .{
+ .size = .one,
+ // TODO: this logic was ported from old code, but it's bogus. This entire block will
+ // go away when https://github.com/ziglang/zig/issues/24061 is implemented anyway.
+ .alignment = switch (vector_ptr_info.flags.alignment) {
+ .none => .none,
+ else => |vec_align| switch (index * elem_ty.abiSize(zcu)) {
+ 0 => vec_align,
+ else => |byte_offset| .minStrict(vec_align, .fromLog2Units(@ctz(byte_offset))),
+ },
+ },
+ .is_const = vector_ptr_info.flags.is_const,
+ .is_volatile = vector_ptr_info.flags.is_volatile,
+ .is_allowzero = vector_ptr_info.flags.is_allowzero,
+ .address_space = vector_ptr_info.flags.address_space,
+ },
+ });
+ if (maybe_vector_ptr_val) |ptr_val| {
+ if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty);
+ const bit_offset = index * @divExact(elem_ty.bitSize(zcu), 8);
+ return .fromValue(try ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt));
+ }
+ break :elem_ptr_ty elem_ptr_ty;
+ };
+
+ if (!init) {
+ try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, vector_ty, vector_ptr_src);
+ try sema.validateRuntimeValue(block, vector_ptr_src, vector_ptr);
+ }
+
+ return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty);
+}
+
+/// Asserts that the layout of the array is already resolved.
fn elemPtrArray(
sema: *Sema,
block: *Block,
@@ -27009,19 +26946,21 @@ fn elemPtrArray(
const pt = sema.pt;
const zcu = pt.zcu;
const array_ptr_ty = sema.typeOf(array_ptr);
+ assert(array_ptr_ty.ptrSize(zcu) == .one);
const array_ty = array_ptr_ty.childType(zcu);
+ assert(array_ty.zigTypeTag(zcu) == .array);
const array_sent = array_ty.sentinel(zcu) != null;
const array_len = array_ty.arrayLen(zcu);
const array_len_s = array_len + @intFromBool(array_sent);
if (array_len_s == 0) {
- return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
+ return sema.fail(block, array_ptr_src, "cannot index into empty array", .{});
}
const maybe_undef_array_ptr_val = sema.resolveValue(array_ptr);
// The index must not be undefined since it can be out of bounds.
- const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
- const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(zcu));
+ const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
+ const index = index_val.toUnsignedInt(zcu);
if (index >= array_len_s) {
const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
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(
break :o index;
} else null;
- if (offset == null and array_ty.zigTypeTag(zcu) == .vector) {
- return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
- }
-
array_ty.assertHasLayout(zcu);
- const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt);
+ const elem_ptr_ty = try array_ptr_ty.elemPtrType(maybe_index, pt);
if (maybe_undef_array_ptr_val) |array_ptr_val| {
if (array_ptr_val.isUndef(zcu)) {
return pt.undefRef(elem_ptr_ty);
}
- if (offset) |index| {
- const elem_ptr = try array_ptr_val.ptrElem(index, pt);
- return Air.internedToRef(elem_ptr.toIntern());
+ if (maybe_index) |index| {
+ return .fromValue(try array_ptr_val.ptrElem(index, pt));
}
}
@@ -27052,7 +26986,7 @@ fn elemPtrArray(
}
// Runtime check is only needed if unable to comptime check.
- if (oob_safety and block.wantSafety() and offset == null) {
+ if (oob_safety and block.wantSafety() and maybe_index == null) {
const len_inst = try pt.intRef(.usize, array_len);
const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
@@ -27075,9 +27009,9 @@ fn elemValSlice(
const pt = sema.pt;
const zcu = pt.zcu;
const slice_ty = sema.typeOf(slice);
+ assert(slice_ty.isSlice(zcu));
const slice_sent = slice_ty.sentinel(zcu) != null;
const elem_ty = slice_ty.childType(zcu);
- var runtime_src = slice_src;
elem_ty.assertHasLayout(zcu);
@@ -27087,7 +27021,6 @@ fn elemValSlice(
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
if (maybe_slice_val) |slice_val| {
- runtime_src = elem_index_src;
const slice_len = slice_val.sliceLen(zcu);
const slice_len_s = slice_len + @intFromBool(slice_sent);
if (slice_len_s == 0) {
@@ -27099,12 +27032,8 @@ fn elemValSlice(
const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
}
- const elem_ptr_ty = try slice_ty.elemPtrType(index, pt);
const elem_ptr_val = try slice_val.ptrElem(index, pt);
- if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
- return Air.internedToRef(elem_val.toIntern());
- }
- runtime_src = slice_src;
+ return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), slice_src);
}
}
@@ -27138,17 +27067,19 @@ fn elemPtrSlice(
const pt = sema.pt;
const zcu = pt.zcu;
const slice_ty = sema.typeOf(slice);
+ assert(slice_ty.isSlice(zcu));
const slice_sent = slice_ty.sentinel(zcu) != null;
-
- slice_ty.childType(zcu).assertHasLayout(zcu);
+ const elem_ty = slice_ty.childType(zcu);
+ elem_ty.assertHasLayout(zcu);
const maybe_undef_slice_val = sema.resolveValue(slice);
// The index must not be undefined since it can be out of bounds.
- const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
- break :o try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(zcu));
+ const offset: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
+ break :o index_val.toUnsignedInt(zcu);
} else null;
const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt);
+ assert(elem_ptr_ty.childType(zcu).toIntern() == elem_ty.toIntern());
if (maybe_undef_slice_val) |slice_val| {
if (slice_val.isUndef(zcu)) {
@@ -27157,15 +27088,14 @@ fn elemPtrSlice(
const slice_len = slice_val.sliceLen(zcu);
const slice_len_s = slice_len + @intFromBool(slice_sent);
if (slice_len_s == 0) {
- return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
+ return sema.fail(block, slice_src, "cannot index into empty slice", .{});
}
if (offset) |index| {
if (index >= slice_len_s) {
const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
}
- const elem_ptr_val = try slice_val.ptrElem(index, pt);
- return Air.internedToRef(elem_ptr_val.toIntern());
+ return .fromValue(try slice_val.ptrElem(index, pt));
}
}
@@ -27182,7 +27112,7 @@ fn elemPtrSlice(
const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
}
- if (slice_ty.childType(zcu).abiSize(zcu) == 0) {
+ if (elem_ty.abiSize(zcu) == 0) {
// zero-bit child type; just extract the pointer and bitcast it
const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice);
return block.addBitCast(elem_ptr_ty, slice_ptr);
@@ -27546,7 +27476,7 @@ fn coerceExtra(
.sentinel = dest_info.sentinel,
});
const empty_array_val = try pt.aggregateValue(empty_array_ty, &.{});
- const empty_array_ptr = try sema.uavRef(empty_array_val.toIntern());
+ const empty_array_ptr = try sema.uavRef(empty_array_val);
return sema.coerceArrayPtrToSlice(block, dest_ty, empty_array_ptr, inst_src);
}
@@ -28499,7 +28429,6 @@ pub fn coerceInMemoryAllowed(
const field_count = dest_ty.structFieldCount(zcu);
for (0..field_count) |field_idx| {
if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple;
- if (dest_ty.resolvedFieldAlignment(field_idx, zcu) != src_ty.resolvedFieldAlignment(field_idx, zcu)) break :tuple;
const dest_field_ty = dest_ty.fieldType(field_idx, zcu);
const src_field_ty = src_ty.fieldType(field_idx, zcu);
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:
fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
const pt = sema.pt;
const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque);
- return Value.fromInterned(try pt.intern(.{ .opt = .{
- .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
- .val = if (opt_val) |val| (try pt.getCoerced(
- Value.fromInterned(try pt.refValue(val.toIntern())),
- ptr_anyopaque_ty,
- )).toIntern() else .none,
+ const opt_ptr_anyopaque_ty = try pt.optionalType(ptr_anyopaque_ty.toIntern());
+ return .fromInterned(try pt.intern(.{ .opt = .{
+ .ty = opt_ptr_anyopaque_ty.toIntern(),
+ .val = payload: {
+ const val = opt_val orelse break :payload .none;
+ const ptr_val = try pt.getCoerced(try pt.uavValue(val), ptr_anyopaque_ty);
+ break :payload ptr_val.toIntern();
+ },
} }));
}
@@ -30078,7 +30009,7 @@ fn analyzeRef(
switch (zcu.intern_pool.indexToKey(val.toIntern())) {
.@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav),
.func => |f| return sema.analyzeNavRef(block, src, f.owner_nav),
- else => return uavRef(sema, val.toIntern()),
+ else => return uavRef(sema, val),
}
}
@@ -30528,7 +30459,7 @@ fn analyzeSlice(
} else ptr_or_slice;
const start = try sema.coerce(block, .usize, uncasted_start, start_src);
- const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
+ const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src);
const new_ptr_ty = sema.typeOf(new_ptr);
// true if and only if the end index of the slice, implicitly or explicitly, equals
@@ -30666,7 +30597,7 @@ fn analyzeSlice(
break :msg msg;
});
}
- return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
+ return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src);
};
const sentinel = s: {
@@ -32118,12 +32049,12 @@ fn resolvePeerTypesInner(
ptr_info.flags.alignment = a: {
// If both alignments are implicit, the result alignment is implicit.
- // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]u32'
+ // e.g. '*u32' + '*c_uint' -> '*u32'
if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) {
break :a .none;
}
// Otherwise (if either alignment is explicit), the result alignment is explicit.
- // e.g. '[*c]u32' + '[*c]align(4) c_uint' -> '[*c]align(4) u32'
+ // e.g. '*u32' + '*align(4) c_uint' -> '*align(4) u32'
const cur_align = switch (ptr_info.flags.alignment) {
.none => Type.fromInterned(ptr_info.child).abiAlignment(zcu),
else => ptr_info.flags.alignment,
@@ -33583,7 +33514,7 @@ fn notePathToComptimeAllocPtr(
else => {}, // there will be another stage
}
- const derivation = try comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema);
+ const derivation = try comptime_ptr.pointerDerivation(arena, pt, sema);
var second_path_aw: std.Io.Writer.Allocating = .init(arena);
defer second_path_aw.deinit();
diff --git a/src/Type.zig b/src/Type.zig
@@ -2337,35 +2337,11 @@ pub fn fieldType(ty: Type, index: usize, zcu: *const Zcu) Type {
return .fromInterned(types.get(ip)[index]);
}
-// TODO MLUGG: clean up doc comments and usages of `{resolved,explicit}FieldAlignment`
-
-/// Returns the alignment of the given struct, tuple, or union field.
-/// Asserts that the layout of `ty` is resolved. Asserts that `ty` is not packed.
-/// Never returns `.none`, even if the field's alignment was not specified.
-pub fn resolvedFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
- switch (ty.explicitFieldAlignment(index, zcu)) {
- .none => {},
- else => |explicit| return explicit,
- }
- const ip = &zcu.intern_pool;
- return switch (ip.indexToKey(ty.toIntern())) {
- .tuple_type => |tuple| Type.fromInterned(tuple.types.get(ip)[index]).abiAlignment(zcu),
- .struct_type => {
- assertHasLayout(ty, zcu);
- const struct_obj = ip.loadStructType(ty.toIntern());
- const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[index]);
- return field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu);
- },
- .union_type => {
- assertHasLayout(ty, zcu);
- const union_obj = ip.loadUnionType(ty.toIntern());
- const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
- return field_ty.abiAlignment(zcu);
- },
- else => unreachable,
- };
-}
-
+/// If an alignment was explicitly specified for the given field of the struct or union type `ty`,
+/// returns that. Otherwise, returns `.none`. This function also supports tuples, for which it
+/// always returns `.none`.
+///
+/// Asserts that the layout of `ty` is resolved, unless `ty` is a tuple.
pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
const ip = &zcu.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
@@ -2388,7 +2364,10 @@ pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment
};
}
-/// Returns the alignment a struct field will have if not explicitly specified.
+/// Returns the alignment a struct field of type `field_ty` will be given if no alignment is
+/// explicitly specified. However, in an `extern struct`, a higher alignment may be available due
+/// to the struct's full layout (i.e. a field might coincidentally be more aligned).
+///
/// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`.
pub fn defaultStructFieldAlignment(
field_ty: Type,
@@ -2664,45 +2643,6 @@ pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } {
return .{ cur_ty, cur_len };
}
-/// Returns a bit-pointer with the same value and a new packed offset.
-pub fn packedStructFieldPtrInfo(
- struct_ty: Type,
- parent_ptr_ty: Type,
- field_idx: u32,
- pt: Zcu.PerThread,
-) InternPool.Key.PtrType.PackedOffset {
- comptime assert(Type.packed_struct_layout_version == 2);
-
- const zcu = pt.zcu;
- const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
-
- var bit_offset: u16 = 0;
- var running_bits: u16 = 0;
- for (0..struct_ty.structFieldCount(zcu)) |i| {
- const f_ty = struct_ty.fieldType(i, zcu);
- if (i == field_idx) {
- bit_offset = running_bits;
- }
- running_bits += @intCast(f_ty.bitSize(zcu));
- }
-
- const res_host_size: u16, const res_bit_offset: u16 = if (parent_ptr_info.packed_offset.host_size != 0) .{
- parent_ptr_info.packed_offset.host_size,
- parent_ptr_info.packed_offset.bit_offset + bit_offset,
- } else .{
- switch (zcu.comp.getZigBackend()) {
- else => (running_bits + 7) / 8,
- .stage2_x86_64, .stage2_c => @intCast(struct_ty.abiSize(zcu)),
- },
- bit_offset,
- };
-
- return .{
- .host_size = res_host_size,
- .bit_offset = res_bit_offset,
- };
-}
-
pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
const ip = &zcu.intern_pool;
var most_aligned_field: u32 = 0;
@@ -2770,88 +2710,225 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
};
}
-/// Returns the type of a pointer to an element.
-/// Asserts that the type is a pointer, and that the element type is indexable.
-/// If the element index is comptime-known, it must be passed in `offset`.
-/// For *@Vector(n, T), return *align(a:b:h:v) T
-/// For *[N]T, return *T
-/// For [*]T, returns *T
-/// For []T, returns *T
-/// Handles const-ness and address spaces in particular.
-/// This code is duplicated in `Sema.analyzePtrArithmetic`.
-/// May perform type resolution and return a transitive `error.AnalysisFail`.
-/// MLUGG TODO audit this shit
-pub fn elemPtrType(ptr_ty: Type, offset: ?usize, pt: Zcu.PerThread) !Type {
+/// Asserts that `ptr_ty` is either a many-item pointer, a slice, a C pointer, or a single pointer
+/// to array (in other words, a pointer which is indexed by pointer arithmetic), and returns the
+/// type of the element pointer at the given index.
+///
+/// Asserts that the layout of the pointer element type is resolved.
+///
+/// If `index` is `null`, the index is an arbitrary runtime-known value.
+pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error!Type {
const zcu = pt.zcu;
- const ptr_info = ptr_ty.ptrInfo(zcu);
+ const ip = &zcu.intern_pool;
+ const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type;
const elem_ty: Type = switch (ptr_info.flags.size) {
- .one => switch (Type.fromInterned(ptr_info.child).zigTypeTag(zcu)) {
- .array, .vector => Type.fromInterned(ptr_info.child).childType(zcu),
- else => .fromInterned(ptr_info.child),
- },
- .many, .c, .slice => .fromInterned(ptr_info.child),
- };
- const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0;
- const parent_ty = ptr_ty.childType(zcu);
-
- const VI = InternPool.Key.PtrType.VectorIndex;
-
- const vector_info: struct {
- host_size: u16 = 0,
- alignment: Alignment = .none,
- vector_index: VI = .none,
- } = if (parent_ty.isVector(zcu) and ptr_info.flags.size == .one) blk: {
- const elem_bits = elem_ty.bitSize(zcu);
- if (elem_bits == 0) break :blk .{};
- const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits);
- if (!is_packed) break :blk .{};
-
- break :blk .{
- .host_size = @intCast(parent_ty.arrayLen(zcu)),
- .alignment = parent_ty.abiAlignment(zcu),
- .vector_index = @enumFromInt(offset.?),
- };
- } else .{};
-
- const alignment: Alignment = a: {
- // Calculate the new pointer alignment.
- if (ptr_info.flags.alignment == .none) {
- // In case of an ABI-aligned pointer, any pointer arithmetic
- // maintains the same ABI-alignedness.
- break :a vector_info.alignment;
- }
- // If the addend is not a comptime-known value we can still count on
- // it being a multiple of the type size.
- const elem_size = elem_ty.abiSize(zcu);
- const addend = if (offset) |off| elem_size * off else elem_size;
-
- // The resulting pointer is aligned to the lcd between the offset (an
- // arbitrary number) and the alignment factor (always a power of two,
- // non zero).
- const new_align: Alignment = @enumFromInt(@min(
- @ctz(addend),
- ptr_info.flags.alignment.toLog2Units(),
- ));
- assert(new_align != .none);
- break :a new_align;
+ .slice, .many, .c => .fromInterned(ptr_info.child),
+ .one => switch (ip.indexToKey(ptr_info.child)) {
+ .array_type => |array_type| .fromInterned(array_type.child),
+ else => unreachable,
+ },
+ };
+ elem_ty.assertHasLayout(zcu);
+ const elem_align: Alignment = switch (elem_ty.classify(zcu)) {
+ .no_possible_value,
+ .one_possible_value,
+ => ptr_info.flags.alignment,
+
+ .partially_comptime,
+ .fully_comptime,
+ => switch (ptr_info.flags.alignment) {
+ .none => .none,
+ else => |array_align| .minStrict(array_align, elem_ty.abiAlignment(zcu)),
+ },
+
+ .runtime => switch (ptr_info.flags.alignment) {
+ .none => .none,
+ else => |array_align| elem_align: {
+ // If the index is runtime-known, use 1 as it gives the minimum possible alignment.
+ const effective_index = index orelse 1;
+ if (effective_index == 0) break :elem_align array_align;
+ const byte_offset = effective_index * elem_ty.abiSize(zcu);
+ break :elem_align .minStrict(array_align, .fromLog2Units(@ctz(byte_offset)));
+ },
+ },
};
return pt.ptrType(.{
.child = elem_ty.toIntern(),
.flags = .{
- .alignment = alignment,
+ .size = .one,
.is_const = ptr_info.flags.is_const,
.is_volatile = ptr_info.flags.is_volatile,
- .is_allowzero = is_allowzero,
+ .is_allowzero = ptr_info.flags.is_allowzero and (index == null or index == 0),
.address_space = ptr_info.flags.address_space,
- .vector_index = vector_info.vector_index,
- },
- .packed_offset = .{
- .host_size = vector_info.host_size,
- .bit_offset = 0,
+ .alignment = elem_align,
},
});
}
+/// Asserts that `ptr_ty` is a pointer (single-item or C) to a struct, union, tuple, or slice, and
+/// returns the type of a pointer to the field at `field_index`.
+///
+/// Asserts that the layout of the pointer child type is resolved.
+///
+/// For slices, `Value.slice_ptr_index` and `Value.slice_len_index` are used for the field index.
+pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator.Error!Type {
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type;
+ assert(ptr_info.flags.size == .one or ptr_info.flags.size == .c);
+ const aggregate_ty: Type = .fromInterned(ptr_info.child);
+ aggregate_ty.assertHasLayout(zcu);
+ // We only exit this `switch` for default-layout aggregates, where the field pointer alignment
+ // is a simple minimum of the aggregate pointer alignment and the field alignment.
+ // `field_align` is `.none` if there is no explicit alignment annotation.
+ const field_ty: Type, const field_align: Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
+ .@"struct" => switch (aggregate_ty.containerLayout(zcu)) {
+ .auto => field: {
+ if (aggregate_ty.isTuple(zcu)) {
+ break :field .{ aggregate_ty.fieldType(field_index, zcu), .none };
+ }
+ const struct_obj = ip.loadStructType(aggregate_ty.toIntern());
+ break :field .{
+ .fromInterned(struct_obj.field_types.get(ip)[field_index]),
+ struct_obj.field_aligns.getOrNone(ip, field_index),
+ };
+ },
+ .@"extern" => {
+ // Field alignment is determined based on the actual field offset. For instance, in
+ // `extern struct { x: u32, y: u16 }`, the `y` field is 4-byte aligned.
+ const field_ty = aggregate_ty.fieldType(field_index, zcu);
+ const field_offset = aggregate_ty.structFieldOffset(field_index, zcu);
+ const parent_align = switch (ptr_info.flags.alignment) {
+ .none => aggregate_ty.abiAlignment(zcu),
+ else => |a| a,
+ };
+ const actual_field_align = switch (field_offset) {
+ 0 => parent_align,
+ else => parent_align.minStrict(.fromLog2Units(@ctz(field_offset))),
+ };
+ const field_ptr_align: Alignment = a: {
+ if (parent_align == .none and
+ aggregate_ty.explicitFieldAlignment(field_index, zcu) == .none and
+ actual_field_align == field_ty.abiAlignment(zcu))
+ {
+ // There's no user-specified 'align' in sight, and the alignment from the
+ // field offset matches the field type's natural alignment, so just use a
+ // default-aligned pointer.
+ break :a .none;
+ }
+ break :a actual_field_align;
+ };
+ var field_ptr_info = ptr_info;
+ field_ptr_info.child = field_ty.toIntern();
+ field_ptr_info.flags.alignment = field_ptr_align;
+ return pt.ptrType(field_ptr_info);
+ },
+ .@"packed" => {
+ var field_ptr_info = ptr_info;
+ if (field_ptr_info.flags.alignment == .none) {
+ field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu);
+ }
+ field_ptr_info.packed_offset = packed_offset: {
+ comptime assert(Type.packed_struct_layout_version == 2);
+ const bit_offset = zcu.structPackedFieldBitOffset(
+ ip.loadStructType(aggregate_ty.toIntern()),
+ field_index,
+ );
+ break :packed_offset if (ptr_info.packed_offset.host_size != 0) .{
+ .host_size = ptr_info.packed_offset.host_size,
+ .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset,
+ } else .{
+ .host_size = switch (zcu.comp.getZigBackend()) {
+ else => @intCast((aggregate_ty.bitSize(zcu) + 7) / 8),
+ .stage2_x86_64, .stage2_c => @intCast(aggregate_ty.abiSize(zcu)),
+ },
+ .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset,
+ };
+ };
+ field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern();
+ return pt.ptrType(field_ptr_info);
+ },
+ },
+ .@"union" => switch (aggregate_ty.containerLayout(zcu)) {
+ .auto => field: {
+ const union_obj = ip.loadUnionType(aggregate_ty.toIntern());
+ break :field .{
+ .fromInterned(union_obj.field_types.get(ip)[field_index]),
+ union_obj.field_aligns.getOrNone(ip, field_index),
+ };
+ },
+ .@"extern" => {
+ // The alignment always matches that of the union pointer. If the union pointer is
+ // default aligned (`.none`), we may need to explicitly align the result pointer.
+ const field_ty = aggregate_ty.fieldType(field_index, zcu);
+ var field_ptr_info = ptr_info;
+ field_ptr_info.child = field_ty.toIntern();
+ if (field_ptr_info.flags.alignment == .none and
+ Alignment.compareStrict(field_ty.abiAlignment(zcu), .neq, aggregate_ty.abiAlignment(zcu)))
+ {
+ field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu);
+ }
+ return pt.ptrType(field_ptr_info);
+ },
+ .@"packed" => {
+ const field_ty = aggregate_ty.fieldType(field_index, zcu);
+ var field_ptr_info = ptr_info;
+ if (field_ptr_info.flags.alignment == .none) {
+ const resolved_align = aggregate_ty.abiAlignment(zcu);
+ if (field_ty.abiAlignment(zcu) != resolved_align) {
+ field_ptr_info.flags.alignment = resolved_align;
+ }
+ }
+ field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern();
+ return pt.ptrType(field_ptr_info);
+ },
+ },
+ .pointer => field: {
+ assert(aggregate_ty.isSlice(zcu));
+ break :field switch (field_index) {
+ Value.slice_ptr_index => .{ aggregate_ty.slicePtrFieldType(zcu), .none },
+ Value.slice_len_index => .{ .usize, .none },
+ else => unreachable,
+ };
+ },
+ else => unreachable,
+ };
+ const field_ptr_align: Alignment = a: {
+ if (aggregate_ty.zigTypeTag(zcu) == .@"struct" and aggregate_ty.structFieldIsComptime(field_index, zcu)) {
+ // For `comptime` fields, just use exactly what was specified, or ABI alignment if nothing was specified.
+ break :a field_align;
+ }
+ const actual_field_align = switch (field_align) {
+ .none => switch (ip.indexToKey(aggregate_ty.toIntern())) {
+ .tuple_type, .union_type => field_ty.abiAlignment(zcu),
+ .struct_type => field_ty.defaultStructFieldAlignment(.auto, zcu),
+ .ptr_type => Type.usize.abiAlignment(zcu),
+ else => unreachable,
+ },
+ else => |a| a,
+ };
+ const actual_aggregate_align = switch (ptr_info.flags.alignment) {
+ .none => aggregate_ty.abiAlignment(zcu),
+ else => |a| a,
+ };
+ if (actual_aggregate_align.compareStrict(.lt, actual_field_align)) {
+ // Underaligned aggregate; use that alignment.
+ assert(ptr_info.flags.alignment != .none);
+ break :a actual_aggregate_align;
+ }
+ if (field_align == .none and actual_field_align == field_ty.abiAlignment(zcu)) {
+ // No explicit annotation on the field (nor an unusual default), and the aggregate
+ // alignment is irrelevant to us, so return an un-annotated pointer.
+ break :a .none;
+ }
+ break :a actual_field_align;
+ };
+ var field_ptr_info = ptr_info;
+ field_ptr_info.flags.alignment = field_ptr_align;
+ field_ptr_info.child = field_ty.toIntern();
+ return pt.ptrType(field_ptr_info);
+}
+
pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTerminatedString {
return switch (ip.indexToKey(ty.toIntern())) {
.struct_type => ip.loadStructType(ty.toIntern()).name,
diff --git a/src/Value.zig b/src/Value.zig
@@ -1687,9 +1687,6 @@ pub fn makeBool(x: bool) Value {
/// `parent_ptr` must be a single-pointer or C pointer to some optional.
///
/// Returns a pointer to the payload of the optional.
-///
-/// May perform type resolution.
-/// MLUGG TODO audit
pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
const zcu = pt.zcu;
const parent_ptr_ty = parent_ptr.typeOf(zcu);
@@ -1715,7 +1712,7 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
}
const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, opt_ty, pt);
- return Value.fromInterned(try pt.intern(.{ .ptr = .{
+ return .fromInterned(try pt.intern(.{ .ptr = .{
.ty = result_ty.toIntern(),
.base_addr = .{ .opt_payload = base_ptr.toIntern() },
.byte_offset = 0,
@@ -1724,8 +1721,6 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
/// `parent_ptr` must be a single-pointer to some error union.
/// Returns a pointer to the payload of the error union.
-/// May perform type resolution.
-/// MLUGG TODO audit
pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
const zcu = pt.zcu;
const parent_ptr_ty = parent_ptr.typeOf(zcu);
@@ -1745,137 +1740,57 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, eu_ty, pt);
- return Value.fromInterned(try pt.intern(.{ .ptr = .{
+ return .fromInterned(try pt.intern(.{ .ptr = .{
.ty = result_ty.toIntern(),
.base_addr = .{ .eu_payload = base_ptr.toIntern() },
.byte_offset = 0,
} }));
}
-// MLUGG TODO: audit ptrField etc in terms of resolution, and probably move them under sema
-
-/// `parent_ptr` must be a single-pointer or c pointer to a struct, union, or slice.
+/// `parent_ptr` must be a single-item pointer or C pointer to a struct, union, or slice.
///
/// Returns a pointer to the aggregate field at the specified index.
///
/// For slices, uses `slice_ptr_index` and `slice_len_index`.
///
-/// May perform type resolution.
+/// Asserts that the layout of the aggregate type is resolved.
pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
const zcu = pt.zcu;
const parent_ptr_ty = parent_ptr.typeOf(zcu);
const aggregate_ty = parent_ptr_ty.childType(zcu);
+ aggregate_ty.assertHasLayout(zcu);
const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
assert(parent_ptr_info.flags.size == .one or parent_ptr_info.flags.size == .c);
- // Exiting this `switch` indicates that the `field` pointer representation should be used.
- const field_ty: Type, const new_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
- .@"struct" => field: {
- const field_ty = aggregate_ty.fieldType(field_idx, zcu);
- switch (aggregate_ty.containerLayout(zcu)) {
- .auto => break :field .{ field_ty, a: {
- if (parent_ptr_info.flags.alignment == .none) {
- break :a aggregate_ty.explicitFieldAlignment(field_idx, zcu);
- }
- const field_align = aggregate_ty.resolvedFieldAlignment(field_idx, zcu);
- break :a field_align.min(parent_ptr_info.flags.alignment);
- } },
- .@"extern" => {
- // Well-defined layout, so just offset the pointer appropriately.
- const byte_off = aggregate_ty.structFieldOffset(field_idx, zcu);
- const field_align: InternPool.Alignment = a: {
- if (byte_off == 0) break :a parent_ptr_info.flags.alignment;
- const true_field_align: InternPool.Alignment = .fromLog2Units(@ctz(byte_off));
- if (parent_ptr_info.flags.alignment == .none and
- true_field_align == field_ty.abiAlignment(zcu))
- {
- break :a .none;
- }
- const parent_align = if (parent_ptr_info.flags.alignment == .none) pa: {
- break :pa aggregate_ty.abiAlignment(zcu);
- } else parent_ptr_info.flags.alignment;
- break :a .minStrict(true_field_align, parent_align);
- };
- const result_ty = try pt.ptrType(info: {
- var new = parent_ptr_info;
- new.child = field_ty.toIntern();
- new.flags.alignment = field_align;
- break :info new;
- });
- return parent_ptr.getOffsetPtr(byte_off, result_ty, pt);
- },
- .@"packed" => {
- const packed_offset = aggregate_ty.packedStructFieldPtrInfo(parent_ptr_ty, field_idx, pt);
- const result_ty = try pt.ptrType(info: {
- var new = parent_ptr_info;
- new.packed_offset = packed_offset;
- new.child = field_ty.toIntern();
- if (new.flags.alignment == .none) {
- new.flags.alignment = aggregate_ty.abiAlignment(zcu);
- }
- break :info new;
- });
- return pt.getCoerced(parent_ptr, result_ty);
- },
- }
- },
- .@"union" => field: {
- const union_obj = zcu.typeToUnion(aggregate_ty).?;
- const field_ty = Type.fromInterned(union_obj.field_types.get(&zcu.intern_pool)[field_idx]);
- switch (aggregate_ty.containerLayout(zcu)) {
- .auto => break :field .{ field_ty, a: {
- if (parent_ptr_info.flags.alignment == .none) {
- break :a aggregate_ty.explicitFieldAlignment(field_idx, zcu);
- }
- const field_align = aggregate_ty.resolvedFieldAlignment(field_idx, zcu);
- break :a field_align.min(parent_ptr_info.flags.alignment);
- } },
- .@"extern" => {
- // Point to the same address.
- const result_ty = try pt.ptrType(info: {
- var new = parent_ptr_info;
- new.child = field_ty.toIntern();
- break :info new;
- });
- return pt.getCoerced(parent_ptr, result_ty);
- },
- .@"packed" => {
- const result_ty = try pt.ptrType(info: {
- var new = parent_ptr_info;
- new.child = field_ty.toIntern();
- break :info new;
- });
- return pt.getCoerced(parent_ptr, result_ty);
- },
- }
+ const field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_idx, pt);
+
+ switch (aggregate_ty.zigTypeTag(zcu)) {
+ .pointer => assert(aggregate_ty.isSlice(zcu)),
+ .@"struct" => switch (aggregate_ty.containerLayout(zcu)) {
+ .auto => {},
+ .@"extern" => return parent_ptr.getOffsetPtr(
+ aggregate_ty.structFieldOffset(field_idx, zcu),
+ field_ptr_ty,
+ pt,
+ ),
+ .@"packed" => return pt.getCoerced(parent_ptr, field_ptr_ty),
},
- .pointer => field_ty: {
- assert(aggregate_ty.isSlice(zcu));
- break :field_ty .{ switch (field_idx) {
- Value.slice_ptr_index => aggregate_ty.slicePtrFieldType(zcu),
- Value.slice_len_index => Type.usize,
- else => unreachable,
- }, switch (parent_ptr_info.flags.alignment) {
- .none => .none,
- else => Type.usize.abiAlignment(zcu).min(parent_ptr_info.flags.alignment),
- } };
+ .@"union" => switch (aggregate_ty.containerLayout(zcu)) {
+ .auto => {},
+ .@"packed", .@"extern" => return pt.getCoerced(parent_ptr, field_ptr_ty),
},
else => unreachable,
- };
+ }
- const result_ty = try pt.ptrType(info: {
- var new = parent_ptr_info;
- new.child = field_ty.toIntern();
- new.flags.alignment = new_align;
- break :info new;
- });
+ // If we get here, we need to use the `.field` comptime pointer representation, because the
+ // aggregate does not have a well-defined layout.
- if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
+ if (parent_ptr.isUndef(zcu)) return pt.undefValue(field_ptr_ty);
const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, aggregate_ty, pt);
- return Value.fromInterned(try pt.intern(.{ .ptr = .{
- .ty = result_ty.toIntern(),
+ return .fromInterned(try pt.intern(.{ .ptr = .{
+ .ty = field_ptr_ty.toIntern(),
.base_addr = .{ .field = .{
.base = base_ptr.toIntern(),
.index = field_idx,
@@ -1884,10 +1799,9 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
} }));
}
-/// `orig_parent_ptr` must be either a single-pointer to an array or vector, or a many-pointer or C-pointer or slice.
+/// `orig_parent_ptr` must be either a single-pointer to an array, a slice, a many-item pointer, or a C pointer.
/// Returns a pointer to the element at the specified index.
-/// May perform type resolution.
-/// MLUGG TODO AUDIT
+/// Asserts that the layout of the pointer element type is resolved.
pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value {
const zcu = pt.zcu;
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
};
const parent_ptr_ty = parent_ptr.typeOf(zcu);
- const elem_ty = parent_ptr_ty.childType(zcu);
- const result_ty = try parent_ptr_ty.elemPtrType(@intCast(field_idx), pt);
+ const result_ty = try parent_ptr_ty.elemPtrType(field_idx, pt);
+ const elem_ty = result_ty.childType(zcu);
+ elem_ty.assertHasLayout(zcu);
if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
- if (result_ty.ptrInfo(zcu).packed_offset.host_size != 0) {
- // Since we have a bit-pointer, the pointer address should be unchanged.
- assert(elem_ty.zigTypeTag(zcu) == .vector);
- return pt.getCoerced(parent_ptr, result_ty);
+ if (!elem_ty.comptimeOnly(zcu)) {
+ const byte_offset = field_idx * elem_ty.abiSize(zcu);
+ return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt);
}
- const PtrStrat = union(enum) {
- offset: u64,
- elem_ptr: Type, // many-ptr elem ty
- };
-
- const strat: PtrStrat = switch (parent_ptr_ty.ptrSize(zcu)) {
- .one => switch (elem_ty.zigTypeTag(zcu)) {
- .vector => .{ .offset = field_idx * @divExact(elem_ty.childType(zcu).bitSize(zcu), 8) },
- .array => strat: {
- const arr_elem_ty = elem_ty.childType(zcu);
- if (arr_elem_ty.comptimeOnly(zcu)) break :strat .{ .elem_ptr = arr_elem_ty };
- break :strat .{ .offset = field_idx * arr_elem_ty.abiSize(zcu) };
- },
- else => unreachable,
- },
-
- .many, .c => if (elem_ty.comptimeOnly(zcu))
- .{ .elem_ptr = elem_ty }
- else
- .{ .offset = field_idx * elem_ty.abiSize(zcu) },
+ // Comptime-only element type.
- .slice => unreachable,
- };
+ if (field_idx == 0) {
+ return pt.getCoerced(parent_ptr, result_ty);
+ }
- switch (strat) {
- .offset => |byte_offset| {
- return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt);
- },
- .elem_ptr => |manyptr_elem_ty| if (field_idx == 0) {
- return pt.getCoerced(parent_ptr, result_ty);
- } else {
- const arr_base_ty, const arr_base_len = manyptr_elem_ty.arrayBase(zcu);
- const base_idx = arr_base_len * field_idx;
- const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr;
- switch (parent_info.base_addr) {
- .arr_elem => |arr_elem| {
- if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) {
- // We already have a pointer to an element of an array of this type.
- // Just modify the index.
- return Value.fromInterned(try pt.intern(.{ .ptr = ptr: {
- var new = parent_info;
- new.base_addr.arr_elem.index += base_idx;
- new.ty = result_ty.toIntern();
- break :ptr new;
- } }));
- }
- },
- else => {},
+ const arr_base_ty, const arr_base_len = elem_ty.arrayBase(zcu);
+ const base_idx = arr_base_len * field_idx;
+ const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr;
+ switch (parent_info.base_addr) {
+ .arr_elem => |arr_elem| {
+ if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) {
+ // We already have a pointer to an element of an array of this type.
+ // Just modify the index.
+ return .fromInterned(try pt.intern(.{ .ptr = ptr: {
+ var new = parent_info;
+ new.base_addr.arr_elem.index += base_idx;
+ new.ty = result_ty.toIntern();
+ break :ptr new;
+ } }));
}
- const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt);
- return Value.fromInterned(try pt.intern(.{ .ptr = .{
- .ty = result_ty.toIntern(),
- .base_addr = .{ .arr_elem = .{
- .base = base_ptr.toIntern(),
- .index = base_idx,
- } },
- .byte_offset = 0,
- } }));
},
+ else => {},
}
+ const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt);
+ return .fromInterned(try pt.intern(.{ .ptr = .{
+ .ty = result_ty.toIntern(),
+ .base_addr = .{ .arr_elem = .{
+ .base = base_ptr.toIntern(),
+ .index = base_idx,
+ } },
+ .byte_offset = 0,
+ } }));
}
fn 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) {
}
};
-pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread) Allocator.Error!PointerDeriveStep {
- return ptr_val.pointerDerivationAdvanced(arena, pt, false, null) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.Canceled => @panic("TODO"), // pls remove from error set mlugg
- error.AnalysisFail => unreachable,
- };
-}
-
/// Given a pointer value, get the sequence of steps to derive it, ideally by taking
/// only field and element pointers with no casts. This can be used by codegen backends
/// which prefer field/elem accesses when lowering constant pointer values.
/// It is also used by the Value printing logic for pointers.
-pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, comptime resolve_types: bool, opt_sema: ?*Sema) !PointerDeriveStep {
+pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, opt_sema: ?*Sema) Allocator.Error!PointerDeriveStep {
// MLUGG TODO: audit tf outta this code
const zcu = pt.zcu;
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
const base_ptr = Value.fromInterned(eu_ptr);
const base_ptr_ty = base_ptr.typeOf(zcu);
const parent_step = try arena.create(PointerDeriveStep);
- parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(eu_ptr), arena, pt, resolve_types, opt_sema);
+ parent_step.* = try pointerDerivation(.fromInterned(eu_ptr), arena, pt, opt_sema);
break :base .{ .eu_payload_ptr = .{
.parent = parent_step,
.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
const base_ptr = Value.fromInterned(opt_ptr);
const base_ptr_ty = base_ptr.typeOf(zcu);
const parent_step = try arena.create(PointerDeriveStep);
- parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(opt_ptr), arena, pt, resolve_types, opt_sema);
+ parent_step.* = try pointerDerivation(.fromInterned(opt_ptr), arena, pt, opt_sema);
break :base .{ .opt_payload_ptr = .{
.parent = parent_step,
.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
.field => |field| base: {
const base_ptr = Value.fromInterned(field.base);
const base_ptr_ty = base_ptr.typeOf(zcu);
- const agg_ty = base_ptr_ty.childType(zcu);
- if (resolve_types) try opt_sema.?.ensureLayoutResolved(agg_ty, .unneeded); // MLUGG TODO: unneeded is a hack
- const field_ty: Type, const field_align: InternPool.Alignment = switch (agg_ty.zigTypeTag(zcu)) {
- .@"struct", .@"union" => .{ agg_ty.fieldType(@intCast(field.index), zcu), agg_ty.resolvedFieldAlignment(@intCast(field.index), pt.zcu) },
- .pointer => switch (field.index) {
- Value.slice_ptr_index => .{ agg_ty.slicePtrFieldType(zcu), Type.ptrAbiAlignment(zcu.getTarget()) },
- Value.slice_len_index => .{ .usize, Type.abiAlignment(.usize, zcu) },
- else => unreachable,
- },
- else => unreachable,
- };
- const base_align = base_ptr_ty.ptrAlignment(zcu);
- const result_align = field_align.minStrict(base_align);
- const result_ty = try pt.ptrType(.{
- .child = field_ty.toIntern(),
- .flags = flags: {
- var flags = base_ptr_ty.ptrInfo(zcu).flags;
- if (result_align == field_ty.abiAlignment(zcu)) {
- flags.alignment = .none;
- } else {
- flags.alignment = result_align;
- }
- break :flags flags;
- },
- });
const parent_step = try arena.create(PointerDeriveStep);
- parent_step.* = try pointerDerivationAdvanced(base_ptr, arena, pt, resolve_types, opt_sema);
+ parent_step.* = try pointerDerivation(base_ptr, arena, pt, opt_sema);
break :base .{ .field_ptr = .{
.parent = parent_step,
.field_idx = @intCast(field.index),
- .result_ptr_ty = result_ty,
+ .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field.index), pt),
} };
},
.arr_elem => |arr_elem| base: {
const parent_step = try arena.create(PointerDeriveStep);
- parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(arr_elem.base), arena, pt, resolve_types, opt_sema);
+ parent_step.* = try pointerDerivation(.fromInterned(arr_elem.base), arena, pt, opt_sema);
const parent_ptr_info = (try parent_step.ptrType(pt)).ptrInfo(zcu);
const result_ptr_ty = try pt.ptrType(.{
.child = parent_ptr_info.child,
.flags = flags: {
var flags = parent_ptr_info.flags;
flags.size = .one;
+ if (flags.alignment != .none) flags.alignment = .minStrict(
+ flags.alignment,
+ Type.fromInterned(parent_ptr_info.child).abiAlignment(zcu),
+ );
break :flags flags;
},
});
@@ -2299,26 +2157,12 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
const end_off = start_off + field_ty.abiSize(zcu);
if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) {
const old_ptr_ty = try cur_derive.ptrType(pt);
- const parent_align = old_ptr_ty.ptrAlignment(zcu);
- const field_align = InternPool.Alignment.fromLog2Units(@min(parent_align.toLog2Units(), @ctz(start_off)));
const parent = try arena.create(PointerDeriveStep);
parent.* = cur_derive;
- const new_ptr_ty = try pt.ptrType(.{
- .child = field_ty.toIntern(),
- .flags = flags: {
- var flags = old_ptr_ty.ptrInfo(zcu).flags;
- if (field_align == field_ty.abiAlignment(zcu)) {
- flags.alignment = .none;
- } else {
- flags.alignment = field_align;
- }
- break :flags flags;
- },
- });
cur_derive = .{ .field_ptr = .{
.parent = parent,
.field_idx = @intCast(field_idx),
- .result_ptr_ty = new_ptr_ty,
+ .result_ptr_ty = try old_ptr_ty.fieldPtrType(@intCast(field_idx), pt),
} };
cur_offset -= start_off;
break;
diff --git a/src/Zcu.zig b/src/Zcu.zig
@@ -3813,9 +3813,9 @@ pub const AtomicPtrAlignmentDiagnostics = struct {
max_bits: u16 = undefined,
};
-/// If ABI alignment of `ty` is OK for atomic operations, returns 0.
-/// Otherwise returns the alignment required on a pointer for the target
-/// to perform atomic operations.
+/// Returns the alignment required for the target to perform atomic operations on type `ty` (that
+/// is, the required align attribute on the pointer). If the ABI alignment of `ty` is sufficient,
+/// returns `.none`.
// TODO this function does not take into account CPU features, which can affect
// this value. Audit this!
pub fn atomicPtrAlignment(
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
@@ -3943,10 +3943,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err
return pt.ptrType(.{
.child = ty,
.flags = .{
- .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu))
- .none
- else
- alignment,
+ .alignment = alignment,
.address_space = @"addrspace",
.is_const = is_const,
},
@@ -4015,23 +4012,24 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
namespace.generation = zcu.generation;
}
-pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index {
- const ptr_ty = (try pt.ptrType(.{
- .child = pt.zcu.intern_pool.typeOf(val),
+pub fn uavValue(pt: Zcu.PerThread, val: Value) Zcu.SemaError!Value {
+ const zcu = pt.zcu;
+ const ptr_ty = try pt.ptrType(.{
+ .child = val.typeOf(zcu).toIntern(),
.flags = .{
.alignment = .none,
.is_const = true,
.address_space = .generic,
},
- })).toIntern();
- return pt.intern(.{ .ptr = .{
- .ty = ptr_ty,
+ });
+ return .fromInterned(try pt.intern(.{ .ptr = .{
+ .ty = ptr_ty.toIntern(),
.base_addr = .{ .uav = .{
- .val = val,
- .orig_ty = ptr_ty,
+ .val = val.toIntern(),
+ .orig_ty = ptr_ty.toIntern(),
} },
.byte_offset = 0,
- } });
+ } }));
}
pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void {
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
@@ -1215,7 +1215,7 @@ pub const DeclGen = struct {
.ptr => {
var arena = std.heap.ArenaAllocator.init(zcu.gpa);
defer arena.deinit();
- const derivation = try val.pointerDerivation(arena.allocator(), pt);
+ const derivation = try val.pointerDerivation(arena.allocator(), pt, null);
try dg.renderPointer(w, derivation, location);
},
.opt => |opt| switch (ctype.info(ctype_pool)) {
diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig
@@ -1038,7 +1038,7 @@ fn constantPtr(cg: *CodeGen, ptr_val: Value) !Id {
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
- const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt);
+ const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, null);
return cg.derivePtr(derivation);
}
diff --git a/src/print_value.zig b/src/print_value.zig
@@ -25,10 +25,7 @@ pub fn formatSema(ctx: FormatContext, writer: *Writer) Writer.Error!void {
const sema = ctx.opt_sema.?;
return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
- error.ComptimeBreak, error.ComptimeReturn => unreachable,
- error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully
- error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
- else => |e| return e,
+ error.WriteFailed => |e| return e,
};
}
@@ -36,9 +33,7 @@ pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void {
std.debug.assert(ctx.opt_sema == null);
return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
- error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,
- error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
- else => |e| return e,
+ error.WriteFailed => |e| return e,
};
}
@@ -48,7 +43,7 @@ pub fn print(
level: u8,
pt: Zcu.PerThread,
opt_sema: ?*Sema,
-) (Writer.Error || Zcu.CompileError)!void {
+) (Writer.Error || Allocator.Error)!void {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
switch (ip.indexToKey(val.toIntern())) {
@@ -212,7 +207,7 @@ fn printAggregate(
level: u8,
pt: Zcu.PerThread,
opt_sema: ?*Sema,
-) (Writer.Error || Zcu.CompileError)!void {
+) (Writer.Error || Allocator.Error)!void {
if (level == 0) {
if (is_ref) try writer.writeByte('&');
return writer.writeAll(".{ ... }");
@@ -307,7 +302,7 @@ fn printPtr(
level: u8,
pt: Zcu.PerThread,
opt_sema: ?*Sema,
-) (Writer.Error || Zcu.CompileError)!void {
+) (Writer.Error || Allocator.Error)!void {
const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
.undef => return writer.writeAll("undefined"),
.ptr => |ptr| ptr,
@@ -332,10 +327,7 @@ fn printPtr(
var arena = std.heap.ArenaAllocator.init(pt.zcu.gpa);
defer arena.deinit();
- const derivation = if (opt_sema) |sema|
- try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, true, sema)
- else
- try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null);
+ const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, opt_sema);
_ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{
.level = level,