diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
index 96fee84ddf2c6459a72b6823d4a0ea513a113270..9fc50c9e5b1ce4be1f0013af0f906cdd8d0920e2 100644
--- a/lib/std/math/big/int.zig
+++ b/lib/std/math/big/int.zig
@@ -924,7 +924,12 @@ pub const Mutable = struct {
/// Asserts the result fits in `r`. Upper bound on the number of limbs needed by
/// r is `calcTwosCompLimbCount(bit_count)`.
pub fn bitReverse(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void {
- if (bit_count == 0) return;
+ if (bit_count == 0) {
+ r.limbs[0] = 0;
+ r.len = 1;
+ r.positive = true;
+ return;
+ }
r.copy(a);
@@ -986,7 +991,12 @@ pub const Mutable = struct {
/// Asserts the result fits in `r`. Upper bound on the number of limbs needed by
/// r is `calcTwosCompLimbCount(8*byte_count)`.
pub fn byteSwap(r: *Mutable, a: Const, signedness: Signedness, byte_count: usize) void {
- if (byte_count == 0) return;
+ if (byte_count == 0) {
+ r.limbs[0] = 0;
+ r.len = 1;
+ r.positive = true;
+ return;
+ }
r.copy(a);
const limbs_required = calcTwosCompLimbCount(8 * byte_count);
diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig
index 165f37edfad0452651ffc51565e1383b67c41a63..c0270a9e03e7fe5eca1e0af9d8fdf36e64fc34e3 100644
--- a/lib/std/zig/Zir.zig
+++ b/lib/std/zig/Zir.zig
@@ -3710,7 +3710,7 @@ pub const Inst = struct {
};
}
- pub fn layout(k: Kind) std.builtin.ContainerLayout {
+ pub fn layout(k: Kind) std.builtin.Type.ContainerLayout {
return switch (k) {
.auto, .tagged_explicit, .tagged_enum, .tagged_enum_explicit => .auto,
.@"extern" => .@"extern",
@@ -4008,20 +4008,6 @@ pub const Inst = struct {
};
};
-/// MLUGG TODO: delete this!
-pub const DeclIterator = struct {
- decls: []const Inst.Index,
- index: usize,
- pub fn next(it: *DeclIterator) ?Inst.Index {
- if (it.index == it.decls.len) return null;
- defer it.index += 1;
- return it.decls[it.index];
- }
-};
-pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
- return .{ .decls = zir.typeDecls(decl_inst), .index = 0 };
-}
-
/// `DeclContents` contains all "interesting" instructions found within a declaration by `findTrackable`.
/// These instructions are partitioned into a few different sets, since this makes ZIR instruction mapping
/// more effective.
diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig
index 34709e17ac9f0e9da4ff212c541e144f09d71da0..f87b93608650fe1e99955695853b2b7aa02acaf9 100644
--- a/lib/std/zig/target.zig
+++ b/lib/std/zig/target.zig
@@ -503,8 +503,7 @@ pub fn intByteSize(target: *const std.Target, bits: u16) u16 {
pub fn intAlignment(target: *const std.Target, bits: u16) u16 {
return switch (target.cpu.arch) {
.x86 => switch (bits) {
- 0 => 0,
- 1...8 => 1,
+ 0...8 => 1,
9...16 => 2,
17...32 => 4,
33...64 => switch (target.os.tag) {
@@ -514,8 +513,7 @@ pub fn intAlignment(target: *const std.Target, bits: u16) u16 {
else => 16,
},
.x86_64 => switch (bits) {
- 0 => 0,
- 1...8 => 1,
+ 0...8 => 1,
9...16 => 2,
17...32 => 4,
33...64 => 8,
diff --git a/src/Air.zig b/src/Air.zig
index 28b7a27ba992f375fffa3a96e7045f5de5952d1e..3f7314e11674a00620f6cf9e0da5d6fbb8671d28 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -14,7 +14,6 @@ const Type = @import("Type.zig");
const Value = @import("Value.zig");
const Zcu = @import("Zcu.zig");
const print = @import("Air/print.zig");
-const types_resolved = @import("Air/types_resolved.zig");
pub const Legalize = @import("Air/Legalize.zig");
pub const Liveness = @import("Air/Liveness.zig");
@@ -173,8 +172,8 @@ pub const Inst = struct {
/// outside the provenance of the operand, the result is undefined.
///
/// Uses the `ty_pl` field. Payload is `Bin`. The lhs is the pointer,
- /// rhs is the offset. Result type is the same as lhs. The operand may
- /// be a slice.
+ /// rhs is the offset. Result type is the same as lhs. The operand type's
+ /// pointer size may be `.slice`, `.many`, or `.c`.
ptr_add,
/// Subtract an offset, in element type units, from a pointer,
/// returning a new pointer. Element type may not be zero bits.
@@ -183,8 +182,8 @@ pub const Inst = struct {
/// outside the provenance of the operand, the result is undefined.
///
/// Uses the `ty_pl` field. Payload is `Bin`. The lhs is the pointer,
- /// rhs is the offset. Result type is the same as lhs. The operand may
- /// be a slice.
+ /// rhs is the offset. Result type is the same as lhs. The operand type's
+ /// pointer size may be `.slice`, `.many`, or `.c`.
ptr_sub,
/// Given two operands which can be floats, integers, or vectors, returns the
/// greater of the operands. For vectors it operates element-wise.
@@ -693,6 +692,7 @@ pub const Inst = struct {
/// Uses the `ty_pl` field with payload `Bin`.
slice_elem_ptr,
/// Given a pointer value, and element index, return the element value at that index.
+ /// The pointer size is either `.c` or `.many`.
/// Result type is the element type of the pointer operand.
/// Uses the `bin_op` field.
ptr_elem_val,
@@ -2440,9 +2440,6 @@ pub fn unwrapShuffleTwo(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index
};
}
-pub const typesFullyResolved = types_resolved.typesFullyResolved;
-pub const typeFullyResolved = types_resolved.checkType;
-pub const valFullyResolved = types_resolved.checkVal;
pub const legalize = Legalize.legalize;
pub const write = print.write;
pub const writeInst = print.writeInst;
diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig
deleted file mode 100644
index 216f690414abdf2daea6993536b303812a05866e..0000000000000000000000000000000000000000
--- a/src/Air/types_resolved.zig
+++ /dev/null
@@ -1,536 +0,0 @@
-const Air = @import("../Air.zig");
-const Zcu = @import("../Zcu.zig");
-const Type = @import("../Type.zig");
-const Value = @import("../Value.zig");
-const InternPool = @import("../InternPool.zig");
-
-/// Given a body of AIR instructions, returns whether all type resolution necessary for codegen is complete.
-/// If `false`, then type resolution must have failed, so codegen cannot proceed.
-pub fn typesFullyResolved(air: Air, zcu: *Zcu) bool {
- return checkBody(air, air.getMainBody(), zcu);
-}
-
-fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
- const tags = air.instructions.items(.tag);
- const datas = air.instructions.items(.data);
-
- for (body) |inst| {
- const data = datas[@intFromEnum(inst)];
- switch (tags[@intFromEnum(inst)]) {
- .inferred_alloc, .inferred_alloc_comptime => unreachable,
-
- .arg => {
- if (!checkType(data.arg.ty.toType(), zcu)) return false;
- },
-
- .add,
- .add_safe,
- .add_optimized,
- .add_wrap,
- .add_sat,
- .sub,
- .sub_safe,
- .sub_optimized,
- .sub_wrap,
- .sub_sat,
- .mul,
- .mul_safe,
- .mul_optimized,
- .mul_wrap,
- .mul_sat,
- .div_float,
- .div_float_optimized,
- .div_trunc,
- .div_trunc_optimized,
- .div_floor,
- .div_floor_optimized,
- .div_exact,
- .div_exact_optimized,
- .rem,
- .rem_optimized,
- .mod,
- .mod_optimized,
- .max,
- .min,
- .bit_and,
- .bit_or,
- .shr,
- .shr_exact,
- .shl,
- .shl_exact,
- .shl_sat,
- .xor,
- .cmp_lt,
- .cmp_lt_optimized,
- .cmp_lte,
- .cmp_lte_optimized,
- .cmp_eq,
- .cmp_eq_optimized,
- .cmp_gte,
- .cmp_gte_optimized,
- .cmp_gt,
- .cmp_gt_optimized,
- .cmp_neq,
- .cmp_neq_optimized,
- .bool_and,
- .bool_or,
- .store,
- .store_safe,
- .set_union_tag,
- .array_elem_val,
- .slice_elem_val,
- .ptr_elem_val,
- .memset,
- .memset_safe,
- .memcpy,
- .memmove,
- .atomic_store_unordered,
- .atomic_store_monotonic,
- .atomic_store_release,
- .atomic_store_seq_cst,
- .legalize_vec_elem_val,
- => {
- if (!checkRef(data.bin_op.lhs, zcu)) return false;
- if (!checkRef(data.bin_op.rhs, zcu)) return false;
- },
-
- .not,
- .bitcast,
- .clz,
- .ctz,
- .popcount,
- .byte_swap,
- .bit_reverse,
- .abs,
- .load,
- .fptrunc,
- .fpext,
- .intcast,
- .intcast_safe,
- .trunc,
- .optional_payload,
- .optional_payload_ptr,
- .optional_payload_ptr_set,
- .wrap_optional,
- .unwrap_errunion_payload,
- .unwrap_errunion_err,
- .unwrap_errunion_payload_ptr,
- .unwrap_errunion_err_ptr,
- .errunion_payload_ptr_set,
- .wrap_errunion_payload,
- .wrap_errunion_err,
- .struct_field_ptr_index_0,
- .struct_field_ptr_index_1,
- .struct_field_ptr_index_2,
- .struct_field_ptr_index_3,
- .get_union_tag,
- .slice_len,
- .slice_ptr,
- .ptr_slice_len_ptr,
- .ptr_slice_ptr_ptr,
- .array_to_slice,
- .int_from_float,
- .int_from_float_optimized,
- .int_from_float_safe,
- .int_from_float_optimized_safe,
- .float_from_int,
- .splat,
- .error_set_has_value,
- .addrspace_cast,
- .c_va_arg,
- .c_va_copy,
- => {
- if (!checkType(data.ty_op.ty.toType(), zcu)) return false;
- if (!checkRef(data.ty_op.operand, zcu)) return false;
- },
-
- .alloc,
- .ret_ptr,
- .c_va_start,
- => {
- if (!checkType(data.ty, zcu)) return false;
- },
-
- .ptr_add,
- .ptr_sub,
- .add_with_overflow,
- .sub_with_overflow,
- .mul_with_overflow,
- .shl_with_overflow,
- .slice,
- .slice_elem_ptr,
- .ptr_elem_ptr,
- => {
- const bin = air.extraData(Air.Bin, data.ty_pl.payload).data;
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- if (!checkRef(bin.lhs, zcu)) return false;
- if (!checkRef(bin.rhs, zcu)) return false;
- },
-
- .block,
- .loop,
- => {
- const block = air.unwrapBlock(inst);
- if (!checkType(block.ty, zcu)) return false;
- if (!checkBody(
- air,
- block.body,
- zcu,
- )) return false;
- },
-
- .dbg_inline_block => {
- const block = air.unwrapDbgBlock(inst);
- if (!checkType(block.ty, zcu)) return false;
- if (!checkBody(
- air,
- block.body,
- zcu,
- )) return false;
- },
-
- .sqrt,
- .sin,
- .cos,
- .tan,
- .exp,
- .exp2,
- .log,
- .log2,
- .log10,
- .floor,
- .ceil,
- .round,
- .trunc_float,
- .neg,
- .neg_optimized,
- .is_null,
- .is_non_null,
- .is_null_ptr,
- .is_non_null_ptr,
- .is_err,
- .is_non_err,
- .is_err_ptr,
- .is_non_err_ptr,
- .ret,
- .ret_safe,
- .ret_load,
- .is_named_enum_value,
- .tag_name,
- .error_name,
- .cmp_lt_errors_len,
- .c_va_end,
- .set_err_return_trace,
- => {
- if (!checkRef(data.un_op, zcu)) return false;
- },
-
- .br, .switch_dispatch => {
- if (!checkRef(data.br.operand, zcu)) return false;
- },
-
- .cmp_vector,
- .cmp_vector_optimized,
- => {
- const extra = air.extraData(Air.VectorCmp, data.ty_pl.payload).data;
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- if (!checkRef(extra.lhs, zcu)) return false;
- if (!checkRef(extra.rhs, zcu)) return false;
- },
-
- .reduce,
- .reduce_optimized,
- => {
- if (!checkRef(data.reduce.operand, zcu)) return false;
- },
-
- .struct_field_ptr,
- .struct_field_val,
- => {
- const extra = air.extraData(Air.StructField, data.ty_pl.payload).data;
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- if (!checkRef(extra.struct_operand, zcu)) return false;
- },
-
- .shuffle_one => {
- const unwrapped = air.unwrapShuffleOne(zcu, inst);
- if (!checkType(unwrapped.result_ty, zcu)) return false;
- if (!checkRef(unwrapped.operand, zcu)) return false;
- for (unwrapped.mask) |m| switch (m.unwrap()) {
- .elem => {},
- .value => |val| if (!checkVal(.fromInterned(val), zcu)) return false,
- };
- },
-
- .shuffle_two => {
- const unwrapped = air.unwrapShuffleTwo(zcu, inst);
- if (!checkType(unwrapped.result_ty, zcu)) return false;
- if (!checkRef(unwrapped.operand_a, zcu)) return false;
- if (!checkRef(unwrapped.operand_b, zcu)) return false;
- // No values to check because there are no comptime-known values other than undef
- },
-
- .cmpxchg_weak,
- .cmpxchg_strong,
- => {
- const extra = air.extraData(Air.Cmpxchg, data.ty_pl.payload).data;
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- if (!checkRef(extra.ptr, zcu)) return false;
- if (!checkRef(extra.expected_value, zcu)) return false;
- if (!checkRef(extra.new_value, zcu)) return false;
- },
-
- .aggregate_init => {
- const ty = data.ty_pl.ty.toType();
- const elems_len: usize = @intCast(ty.arrayLen(zcu));
- const elems: []const Air.Inst.Ref = @ptrCast(air.extra.items[data.ty_pl.payload..][0..elems_len]);
- if (!checkType(ty, zcu)) return false;
- if (ty.zigTypeTag(zcu) == .@"struct") {
- for (elems, 0..) |elem, elem_idx| {
- if (ty.structFieldIsComptime(elem_idx, zcu)) continue;
- if (!checkRef(elem, zcu)) return false;
- }
- } else {
- for (elems) |elem| {
- if (!checkRef(elem, zcu)) return false;
- }
- }
- },
-
- .union_init => {
- const extra = air.extraData(Air.UnionInit, data.ty_pl.payload).data;
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- if (!checkRef(extra.init, zcu)) return false;
- },
-
- .field_parent_ptr => {
- const extra = air.extraData(Air.FieldParentPtr, data.ty_pl.payload).data;
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- if (!checkRef(extra.field_ptr, zcu)) return false;
- },
-
- .atomic_load => {
- if (!checkRef(data.atomic_load.ptr, zcu)) return false;
- },
-
- .prefetch => {
- if (!checkRef(data.prefetch.ptr, zcu)) return false;
- },
-
- .runtime_nav_ptr => {
- if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false;
- },
-
- .select,
- .mul_add,
- .legalize_vec_store_elem,
- => {
- const bin = air.extraData(Air.Bin, data.pl_op.payload).data;
- if (!checkRef(data.pl_op.operand, zcu)) return false;
- if (!checkRef(bin.lhs, zcu)) return false;
- if (!checkRef(bin.rhs, zcu)) return false;
- },
-
- .atomic_rmw => {
- const extra = air.extraData(Air.AtomicRmw, data.pl_op.payload).data;
- if (!checkRef(data.pl_op.operand, zcu)) return false;
- if (!checkRef(extra.operand, zcu)) return false;
- },
-
- .call,
- .call_always_tail,
- .call_never_tail,
- .call_never_inline,
- => {
- const call = air.unwrapCall(inst);
- const args = call.args;
- if (!checkRef(call.callee, zcu)) return false;
- for (args) |arg| if (!checkRef(arg, zcu)) return false;
- },
-
- .dbg_var_ptr,
- .dbg_var_val,
- .dbg_arg_inline,
- => {
- if (!checkRef(data.pl_op.operand, zcu)) return false;
- },
-
- .@"try", .try_cold => {
- const unwrapped_try = air.unwrapTry(inst);
- if (!checkRef(unwrapped_try.error_union, zcu)) return false;
- if (!checkBody(
- air,
- unwrapped_try.else_body,
- zcu,
- )) return false;
- },
-
- .try_ptr, .try_ptr_cold => {
- const unwrapped_try = air.unwrapTryPtr(inst);
- if (!checkType(unwrapped_try.error_union_payload_ptr_ty.toType(), zcu)) return false;
- if (!checkRef(unwrapped_try.error_union_ptr, zcu)) return false;
- if (!checkBody(
- air,
- unwrapped_try.else_body,
- zcu,
- )) return false;
- },
-
- .cond_br => {
- const cond_br = air.unwrapCondBr(inst);
- if (!checkRef(cond_br.condition, zcu)) return false;
- if (!checkBody(
- air,
- cond_br.then_body,
- zcu,
- )) return false;
- if (!checkBody(
- air,
- cond_br.else_body,
- zcu,
- )) return false;
- },
-
- .switch_br, .loop_switch_br => {
- const switch_br = air.unwrapSwitch(inst);
- if (!checkRef(switch_br.operand, zcu)) return false;
- var it = switch_br.iterateCases();
- while (it.next()) |case| {
- for (case.items) |item| if (!checkRef(item, zcu)) return false;
- for (case.ranges) |range| {
- if (!checkRef(range[0], zcu)) return false;
- if (!checkRef(range[1], zcu)) return false;
- }
- if (!checkBody(air, case.body, zcu)) return false;
- }
- if (!checkBody(air, it.elseBody(), zcu)) return false;
- },
-
- .assembly => {
- const unwrapped_asm = air.unwrapAsm(inst);
- if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
- // Luckily, we only care about the inputs and outputs, so we don't have to do
- // the whole null-terminated string dance.
- const outputs = unwrapped_asm.outputs;
- const inputs = unwrapped_asm.inputs;
-
- for (outputs) |output| if (output != .none and !checkRef(output, zcu)) return false;
- for (inputs) |input| if (input != .none and !checkRef(input, zcu)) return false;
- },
-
- .legalize_compiler_rt_call => {
- const rt_call = air.unwrapCompilerRtCall(inst);
- const args = rt_call.args;
- for (args) |arg| if (!checkRef(arg, zcu)) return false;
- },
-
- .trap,
- .breakpoint,
- .ret_addr,
- .frame_addr,
- .unreach,
- .wasm_memory_size,
- .wasm_memory_grow,
- .work_item_id,
- .work_group_size,
- .work_group_id,
- .dbg_stmt,
- .dbg_empty_stmt,
- .err_return_trace,
- .save_err_return_trace_index,
- .repeat,
- => {},
- }
- }
- return true;
-}
-
-fn checkRef(ref: Air.Inst.Ref, zcu: *Zcu) bool {
- const ip_index = ref.toInterned() orelse {
- // This operand refers back to a previous instruction.
- // We have already checked that instruction's type.
- // So, there's no need to check this operand's type.
- return true;
- };
- return checkVal(Value.fromInterned(ip_index), zcu);
-}
-
-pub fn checkVal(val: Value, zcu: *Zcu) bool {
- const ty = val.typeOf(zcu);
- if (!checkType(ty, zcu)) return false;
- if (val.isUndef(zcu)) return true;
- if (ty.toIntern() == .type_type and !checkType(val.toType(), zcu)) return false;
- // Check for lazy values
- switch (zcu.intern_pool.indexToKey(val.toIntern())) {
- .int => |int| switch (int.storage) {
- .u64, .i64, .big_int => return true,
- .lazy_align, .lazy_size => |ty_index| {
- return checkType(Type.fromInterned(ty_index), zcu);
- },
- },
- else => return true,
- }
-}
-
-pub fn checkType(ty: Type, zcu: *Zcu) bool {
- const ip = &zcu.intern_pool;
- if (ty.isGenericPoison()) return true;
- return switch (ty.zigTypeTag(zcu)) {
- .type,
- .void,
- .bool,
- .noreturn,
- .int,
- .float,
- .error_set,
- .@"enum",
- .@"opaque",
- .vector,
- // These types can appear due to some dummy instructions Sema introduces and expects to be omitted by Liveness.
- // It's a little silly -- but fine, we'll return `true`.
- .comptime_float,
- .comptime_int,
- .undefined,
- .null,
- .enum_literal,
- => true,
-
- .frame,
- .@"anyframe",
- => @panic("TODO Air.types_resolved.checkType async frames"),
-
- .optional => checkType(ty.childType(zcu), zcu),
- .error_union => checkType(ty.errorUnionPayload(zcu), zcu),
- .pointer => checkType(ty.childType(zcu), zcu),
- .array => checkType(ty.childType(zcu), zcu),
-
- .@"fn" => {
- const info = zcu.typeToFunc(ty).?;
- for (0..info.param_types.len) |i| {
- const param_ty = info.param_types.get(ip)[i];
- if (!checkType(Type.fromInterned(param_ty), zcu)) return false;
- }
- return checkType(Type.fromInterned(info.return_type), zcu);
- },
- .@"struct" => switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => {
- const struct_obj = zcu.typeToStruct(ty).?;
- return switch (struct_obj.layout) {
- .@"packed" => struct_obj.backingIntTypeUnordered(ip) != .none,
- .auto, .@"extern" => struct_obj.flagsUnordered(ip).fully_resolved,
- };
- },
- .tuple_type => |tuple| {
- for (0..tuple.types.len) |i| {
- const field_is_comptime = tuple.values.get(ip)[i] != .none;
- if (field_is_comptime) continue;
- const field_ty = tuple.types.get(ip)[i];
- if (!checkType(Type.fromInterned(field_ty), zcu)) return false;
- }
- return true;
- },
- else => unreachable,
- },
- .@"union" => return zcu.typeToUnion(ty).?.flagsUnordered(ip).status == .fully_resolved,
- };
-}
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 521de0b427c4a9cba23daab8e46521bc1ec6ef59..14046f38fc2e41c9aa50efa7586f6815ba67aeb6 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -126,15 +126,7 @@ oneshot_prelink_tasks: std.ArrayList(link.PrelinkTask),
/// work is queued or not.
queued_jobs: QueuedJobs,
-work_queues: [
- len: {
- var len: usize = 0;
- for (std.enums.values(Job.Tag)) |tag| {
- len = @max(Job.stage(tag) + 1, len);
- }
- break :len len;
- }
-]std.Deque(Job),
+work_queues: [2]std.Deque(Job),
/// These jobs are to invoke the Clang compiler to create an object file, which
/// gets linked with the Compilation.
@@ -990,35 +982,27 @@ const Job = union(enum) {
update_line_number: InternPool.TrackedInst.Index,
/// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
/// This may be its first time being analyzed, or it may be outdated.
- /// If the unit is a test function, an `analyze_func` job will then be queued.
- analyze_comptime_unit: InternPool.AnalUnit,
- /// This function must be semantically analyzed.
- /// This may be its first time being analyzed, or it may be outdated.
- /// After analysis, a `codegen_func` job will be queued.
- /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
- /// This job is separate from `analyze_comptime_unit` because it has a different priority.
- analyze_func: InternPool.Index,
+ /// If the unit is a function, a `codegen_func` job will be queued after analysis completes.
+ /// If the unit is a *test* function, an `analyze_func` job will also be queued.
+ analyze_unit: InternPool.AnalUnit,
/// The main source file for the module needs to be analyzed.
analyze_mod: *Package.Module,
- /// Fully resolve the given `struct` or `union` type.
- resolve_type_fully: InternPool.Index,
/// The value is the index into `windows_libs`.
windows_import_lib: usize,
- const Tag = @typeInfo(Job).@"union".tag_type.?;
- fn stage(tag: Tag) usize {
- return switch (tag) {
- // Prioritize functions so that codegen can get to work on them on a
- // separate thread, while Sema goes back to its own work.
- .resolve_type_fully, .analyze_func, .codegen_func => 0,
+ fn stage(job: *const Job) usize {
+ // Prioritize functions so that codegen can get to work on them on a
+ // separate thread, while Sema goes back to its own work.
+ return switch (job.*) {
+ .codegen_func => 0,
+ .analyze_unit => |unit| switch (unit.unwrap()) {
+ .func => 0,
+ else => 1,
+ },
else => 1,
};
}
- comptime {
- // Job dependencies
- assert(stage(.resolve_type_fully) <= stage(.codegen_func));
- }
};
pub const CObject = struct {
@@ -3728,7 +3712,9 @@ const Header = extern struct {
src_hash_deps_len: u32,
nav_val_deps_len: u32,
nav_ty_deps_len: u32,
- interned_deps_len: u32,
+ type_layout_deps_len: u32,
+ type_inits_deps_len: u32,
+ func_ies_deps_len: u32,
zon_file_deps_len: u32,
embed_file_deps_len: u32,
namespace_deps_len: u32,
@@ -3776,7 +3762,9 @@ pub fn saveState(comp: *Compilation) !void {
.src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
.nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
.nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
- .interned_deps_len = @intCast(ip.interned_deps.count()),
+ .type_layout_deps_len = @intCast(ip.type_layout_deps.count()),
+ .type_inits_deps_len = @intCast(ip.type_inits_deps.count()),
+ .func_ies_deps_len = @intCast(ip.func_ies_deps.count()),
.zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
.embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
@@ -3800,7 +3788,7 @@ pub fn saveState(comp: *Compilation) !void {
},
});
- try bufs.ensureTotalCapacityPrecise(22 + 9 * pt_headers.items.len);
+ try bufs.ensureTotalCapacityPrecise(26 + 9 * pt_headers.items.len);
addBuf(&bufs, mem.asBytes(&header));
addBuf(&bufs, @ptrCast(pt_headers.items));
@@ -3810,8 +3798,12 @@ pub fn saveState(comp: *Compilation) !void {
addBuf(&bufs, @ptrCast(ip.nav_val_deps.values()));
addBuf(&bufs, @ptrCast(ip.nav_ty_deps.keys()));
addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values()));
- addBuf(&bufs, @ptrCast(ip.interned_deps.keys()));
- addBuf(&bufs, @ptrCast(ip.interned_deps.values()));
+ addBuf(&bufs, @ptrCast(ip.type_layout_deps.keys()));
+ addBuf(&bufs, @ptrCast(ip.type_layout_deps.values()));
+ addBuf(&bufs, @ptrCast(ip.type_inits_deps.keys()));
+ addBuf(&bufs, @ptrCast(ip.type_inits_deps.values()));
+ addBuf(&bufs, @ptrCast(ip.func_ies_deps.keys()));
+ addBuf(&bufs, @ptrCast(ip.func_ies_deps.values()));
addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys()));
addBuf(&bufs, @ptrCast(ip.zon_file_deps.values()));
addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys()));
@@ -4489,7 +4481,7 @@ pub fn addModuleErrorMsg(
const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
.@"comptime" => "comptime",
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
+ .type_layout, .type_inits => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
.memoized_state => null,
};
@@ -4900,15 +4892,7 @@ fn performAllTheWork(
// If there's no work queued, check if there's anything outdated
// which we need to work on, and queue it if so.
if (try zcu.findOutdatedToAnalyze()) |outdated| {
- try comp.queueJob(switch (outdated.unwrap()) {
- .func => |f| .{ .analyze_func = f },
- .memoized_state,
- .@"comptime",
- .nav_ty,
- .nav_val,
- .type,
- => .{ .analyze_comptime_unit = outdated },
- });
+ try comp.queueJob(.{ .analyze_unit = outdated });
continue;
}
zcu.sema_prog_node.end();
@@ -5151,7 +5135,7 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node
const JobError = Allocator.Error || Io.Cancelable;
pub fn queueJob(comp: *Compilation, job: Job) !void {
- try comp.work_queues[Job.stage(job)].pushBack(comp.gpa, job);
+ try comp.work_queues[job.stage()].pushBack(comp.gpa, job);
}
pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void {
@@ -5166,13 +5150,24 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v
var owned_air: ?Air = func.air;
defer if (owned_air) |*air| air.deinit(gpa);
- if (!owned_air.?.typesFullyResolved(zcu)) {
- // Type resolution failed in a way which affects this function. This is a transitive
- // failure, but it doesn't need recording, because this function semantically depends
- // on the failed type, so when it is changed the function is updated.
- zcu.codegen_prog_node.completeOne();
- comp.link_prog_node.completeOne();
- return;
+ {
+ const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
+ defer pt.deactivate();
+ pt.resolveAirTypesForCodegen(&owned_air.?) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+
+ error.AnalysisFail => {
+ // Type resolution failed, making codegen of this function impossible. This
+ // is a transitive failure, but it doesn't need recording, because this
+ // function semantically depends on the failed type, so when it is changed
+ // the function will be updated.
+ zcu.codegen_prog_node.completeOne();
+ comp.link_prog_node.completeOne();
+ return;
+ },
+ };
}
// Some linkers need to refer to the AIR. In that case, the linker is not running
@@ -5198,45 +5193,54 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v
}
}
assert(nav.status == .fully_resolved);
- if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) {
- // Type resolution failed in a way which affects this `Nav`. This is a transitive
- // failure, but it doesn't need recording, because this `Nav` semantically depends
- // on the failed type, so when it is changed the `Nav` will be updated.
- comp.link_prog_node.completeOne();
- return;
+ {
+ const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
+ defer pt.deactivate();
+ pt.resolveValueTypesForCodegen(zcu.navValue(nav_index)) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+
+ error.AnalysisFail => {
+ // Type resolution failed, making codegen of this `Nav` impossible. This is
+ // a transitive failure, but it doesn't need recording, because this `Nav`
+ // semantically depends on the failed type, so when it is changed the value
+ // of the `Nav` will be updated.
+ comp.link_prog_node.completeOne();
+ return;
+ },
+ };
}
try comp.link_queue.enqueueZcu(comp, tid, .{ .link_nav = nav_index });
},
.link_type => |ty| {
const zcu = comp.zcu.?;
if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa);
- if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) {
- // Type resolution failed in a way which affects this type. This is a transitive
- // failure, but it doesn't need recording, because this type semantically depends
- // on the failed type, so when that is changed, this type will be updated.
- comp.link_prog_node.completeOne();
- return;
+ {
+ const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
+ defer pt.deactivate();
+ pt.resolveTypeForCodegen(.fromInterned(ty)) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+
+ error.AnalysisFail => {
+ // Type resolution failed, making codegen of this type impossible. This is
+ // a transitive failure, but it doesn't need recording, because this type
+ // semantically depends on the failed type, so when it is changed the type
+ // will be updated appropriately.
+ comp.link_prog_node.completeOne();
+ return;
+ },
+ };
}
try comp.link_queue.enqueueZcu(comp, tid, .{ .link_type = ty });
},
.update_line_number => |tracked_inst| {
try comp.link_queue.enqueueZcu(comp, tid, .{ .update_line_number = tracked_inst });
},
- .analyze_func => |func| {
- const tracy_trace = traceNamed(@src(), "analyze_func");
- defer tracy_trace.end();
-
- const pt: Zcu.PerThread = .activate(comp.zcu.?, tid);
- defer pt.deactivate();
-
- pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.Canceled => |e| return e,
- error.AnalysisFail => return,
- };
- },
- .analyze_comptime_unit => |unit| {
- const tracy_trace = traceNamed(@src(), "analyze_comptime_unit");
+ .analyze_unit => |unit| {
+ const tracy_trace = traceNamed(@src(), "analyze_unit");
defer tracy_trace.end();
const pt: Zcu.PerThread = .activate(comp.zcu.?, tid);
@@ -5246,9 +5250,10 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v
.@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
.nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
.nav_val => |nav| pt.ensureNavValUpToDate(nav),
- .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
+ .type_layout => |ty| pt.ensureTypeLayoutUpToDate(.fromInterned(ty)),
+ .type_inits => |ty| pt.ensureTypeInitsUpToDate(.fromInterned(ty)),
.memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage),
- .func => unreachable,
+ .func => |func| pt.ensureFuncBodyUpToDate(func),
};
maybe_err catch |err| switch (err) {
error.OutOfMemory => |e| return e,
@@ -5275,27 +5280,15 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v
try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
}
},
- .resolve_type_fully => |ty| {
- const tracy_trace = traceNamed(@src(), "resolve_type_fully");
- defer tracy_trace.end();
-
- const pt: Zcu.PerThread = .activate(comp.zcu.?, tid);
- defer pt.deactivate();
- Type.fromInterned(ty).resolveFully(pt) catch |err| switch (err) {
- error.OutOfMemory, error.Canceled => |e| return e,
- error.AnalysisFail => return,
- };
- },
.analyze_mod => |mod| {
const tracy_trace = traceNamed(@src(), "analyze_mod");
defer tracy_trace.end();
const pt: Zcu.PerThread = .activate(comp.zcu.?, tid);
defer pt.deactivate();
- pt.semaMod(mod) catch |err| switch (err) {
- error.OutOfMemory, error.Canceled => |e| return e,
- error.AnalysisFail => return,
- };
+
+ const mod_root_file = pt.zcu.module_roots.get(mod).?.unwrap().?;
+ try pt.ensureFileAnalyzed(mod_root_file);
},
.windows_import_lib => |index| {
const tracy_trace = traceNamed(@src(), "windows_import_lib");
diff --git a/src/IncrementalDebugServer.zig b/src/IncrementalDebugServer.zig
index b4bc2c812ebd8ad67dce61738989efca3f388058..ce40844057d8e34d409f6ba78eb1b6b59f9085dc 100644
--- a/src/IncrementalDebugServer.zig
+++ b/src/IncrementalDebugServer.zig
@@ -306,12 +306,8 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const
try w.print("[{d}] ", .{i});
switch (dependee) {
.src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}),
- .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }),
- .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
- .struct_type, .union_type, .enum_type => try w.print("type {d}", .{@intFromEnum(ip_index)}),
- .func => try w.print("func {d}", .{@intFromEnum(ip_index)}),
- else => unreachable,
- },
+ .nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @intFromEnum(nav) }),
+ .type_layout, .type_inits, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @intFromEnum(ip_index) }),
.memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}),
}
try w.writeByte('\n');
@@ -376,8 +372,10 @@ fn parseAnalUnit(str: []const u8) ?AnalUnit {
return .wrap(.{ .nav_val = @enumFromInt(parseIndex(idx_str) orelse return null) });
} else if (std.mem.eql(u8, kind, "nav_ty")) {
return .wrap(.{ .nav_ty = @enumFromInt(parseIndex(idx_str) orelse return null) });
- } else if (std.mem.eql(u8, kind, "type")) {
- return .wrap(.{ .type = @enumFromInt(parseIndex(idx_str) orelse return null) });
+ } else if (std.mem.eql(u8, kind, "type_layout")) {
+ return .wrap(.{ .type_layout = @enumFromInt(parseIndex(idx_str) orelse return null) });
+ } else if (std.mem.eql(u8, kind, "type_inits")) {
+ return .wrap(.{ .type_inits = @enumFromInt(parseIndex(idx_str) orelse return null) });
} else if (std.mem.eql(u8, kind, "func")) {
return .wrap(.{ .func = @enumFromInt(parseIndex(idx_str) orelse return null) });
} else if (std.mem.eql(u8, kind, "memoized_state")) {
diff --git a/src/InternPool.zig b/src/InternPool.zig
index 595bedd5473653ccc2885b3181d5007dfe0b926e..ed4666a5b91a940bab9f91146c2095911c0909ef 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -47,11 +47,15 @@ nav_val_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
/// Dependencies on the type of a Nav.
/// Value is index into `dep_entries` of the first dependency on this Nav value.
nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
-/// Dependencies on an interned value, either:
-/// * a runtime function (invalidated when its IES changes)
-/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
-/// Value is index into `dep_entries` of the first dependency on this interned value.
-interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
+/// Dependencies on a function's inferred error set. Key is the function body, not the IES.
+/// Value is index into `dep_entries` of the first dependency on this function's IES.
+func_ies_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
+/// Dependencies on the resolved layout of a `struct` or `union` type.
+/// Value is index into `dep_entries` of the first dependency on this type's layout.
+type_layout_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
+/// Dependencies on the resolved initializers of a `struct` or `enum` type.
+/// Value is index into `dep_entries` of the first dependency on this type's inits.
+type_inits_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
/// Dependencies on a ZON file. Triggered by `@import` of ZON.
/// Value is index into `dep_entries` of the first dependency on this ZON file.
zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
@@ -104,7 +108,9 @@ pub const empty: InternPool = .{
.src_hash_deps = .empty,
.nav_val_deps = .empty,
.nav_ty_deps = .empty,
- .interned_deps = .empty,
+ .func_ies_deps = .empty,
+ .type_layout_deps = .empty,
+ .type_inits_deps = .empty,
.zon_file_deps = .empty,
.embed_file_deps = .empty,
.namespace_deps = .empty,
@@ -415,7 +421,8 @@ pub const AnalUnit = packed struct(u64) {
@"comptime",
nav_val,
nav_ty,
- type,
+ type_layout,
+ type_inits,
func,
memoized_state,
};
@@ -427,9 +434,11 @@ pub const AnalUnit = packed struct(u64) {
nav_val: Nav.Index,
/// This `AnalUnit` resolves the type of the given `Nav`.
nav_ty: Nav.Index,
- /// This `AnalUnit` resolves the given `struct`/`union`/`enum` type.
- /// Generated tag enums are never used here (they do not undergo type resolution).
- type: InternPool.Index,
+ /// This `AnalUnit` resolves the layout of the given `struct` or `union` type.
+ type_layout: InternPool.Index,
+ /// This `AnalUnit` resolves the field inits of the given `struct` or `enum` type.
+ /// The type may be a union's auto-generated tag enum, if the union has explicit field values.
+ type_inits: InternPool.Index,
/// This `AnalUnit` analyzes the body of the given runtime function.
func: InternPool.Index,
/// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.
@@ -840,7 +849,10 @@ pub const Dependee = union(enum) {
src_hash: TrackedInst.Index,
nav_val: Nav.Index,
nav_ty: Nav.Index,
- interned: Index,
+ /// Index is the function, not its IES.
+ func_ies: Index,
+ type_layout: Index,
+ type_inits: Index,
zon_file: FileIndex,
embed_file: Zcu.EmbedFile.Index,
namespace: TrackedInst.Index,
@@ -892,7 +904,9 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
.src_hash => |x| ip.src_hash_deps.get(x),
.nav_val => |x| ip.nav_val_deps.get(x),
.nav_ty => |x| ip.nav_ty_deps.get(x),
- .interned => |x| ip.interned_deps.get(x),
+ .func_ies => |x| ip.func_ies_deps.get(x),
+ .type_layout => |x| ip.type_layout_deps.get(x),
+ .type_inits => |x| ip.type_inits_deps.get(x),
.zon_file => |x| ip.zon_file_deps.get(x),
.embed_file => |x| ip.embed_file_deps.get(x),
.namespace => |x| ip.namespace_deps.get(x),
@@ -965,7 +979,9 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
.src_hash => ip.src_hash_deps,
.nav_val => ip.nav_val_deps,
.nav_ty => ip.nav_ty_deps,
- .interned => ip.interned_deps,
+ .func_ies => ip.func_ies_deps,
+ .type_layout => ip.type_layout_deps,
+ .type_inits => ip.type_inits_deps,
.zon_file => ip.zon_file_deps,
.embed_file => ip.embed_file_deps,
.namespace => ip.namespace_deps,
@@ -2065,15 +2081,15 @@ pub const Key = union(enum) {
simple_type: SimpleType,
/// This represents a struct that has been explicitly declared in source code,
/// or was created with `@Struct`. It is unique and based on a declaration.
- struct_type: NamespaceType,
+ struct_type: ContainerType,
/// This is a tuple type. Tuples are logically similar to structs, but have some
/// important differences in semantics; they do not undergo staged type resolution,
/// so cannot be self-referential, and they are not considered container/namespace
/// types, so cannot have declarations and have structural equality properties.
tuple_type: TupleType,
- union_type: NamespaceType,
- opaque_type: NamespaceType,
- enum_type: NamespaceType,
+ union_type: ContainerType,
+ opaque_type: ContainerType,
+ enum_type: ContainerType,
func_type: FuncType,
error_set_type: ErrorSetType,
/// The payload is the function body, either a `func_decl` or `func_instance`.
@@ -2211,16 +2227,10 @@ pub const Key = union(enum) {
/// * `loadUnionType`
/// * `loadEnumType`
/// * `loadOpaqueType`
- pub const NamespaceType = union(enum) {
+ pub const ContainerType = union(enum) {
/// This type corresponds to an actual source declaration, e.g. `struct { ... }`.
/// It is hashed based on its ZIR instruction index and set of captures.
declared: Declared,
- /// This type is an automatically-generated enum tag type for a union.
- /// It is hashed based on the index of the union type it corresponds to.
- generated_tag: struct {
- /// The union for which this is a tag type.
- union_type: Index,
- },
/// This type originates from a reification via `@Enum`, `@Struct`, `@Union` or from an anonymous initialization.
/// It is hashed based on its ZIR instruction index and fields, attributes, etc.
/// To avoid making this key overly complex, the type-specific data is hashed by Sema.
@@ -2231,10 +2241,17 @@ pub const Key = union(enum) {
/// A hash of this type's attributes, fields, etc, generated by Sema.
type_hash: u64,
},
+ /// This type is an automatically-generated enum tag type for this union type.
+ /// It is hashed based on the index of the union type it corresponds to.
+ generated_union_tag: Index,
pub const Declared = struct {
/// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction.
zir_index: TrackedInst.Index,
+ /// If the type declaration had an argument type (tag type or packed backing type), this
+ /// is that type. Otherwise, this is `.none`. It is always `.none` for `opaque` types as
+ /// `opaque(T)` does not exist.
+ arg_ty: Index,
/// The captured values of this type. These values must be fully resolved per the language spec.
captures: union(enum) {
owned: CaptureValue.Slice,
@@ -2254,7 +2271,6 @@ pub const Key = union(enum) {
noalias_bits: u32,
cc: std.builtin.CallingConvention,
is_var_args: bool,
- is_generic: bool,
is_noinline: bool,
pub fn paramIsComptime(self: @This(), i: u5) bool {
@@ -2273,7 +2289,6 @@ pub const Key = union(enum) {
a.comptime_bits == b.comptime_bits and
a.noalias_bits == b.noalias_bits and
a.is_var_args == b.is_var_args and
- a.is_generic == b.is_generic and
a.is_noinline == b.is_noinline and
std.meta.eql(a.cc, b.cc);
}
@@ -2287,7 +2302,6 @@ pub const Key = union(enum) {
std.hash.autoHash(hasher, self.noalias_bits);
std.hash.autoHash(hasher, self.cc);
std.hash.autoHash(hasher, self.is_var_args);
- std.hash.autoHash(hasher, self.is_generic);
std.hash.autoHash(hasher, self.is_noinline);
}
};
@@ -2471,8 +2485,6 @@ pub const Key = union(enum) {
u64: u64,
i64: i64,
big_int: BigIntConst,
- lazy_align: Index,
- lazy_size: Index,
/// Big enough to fit any non-BigInt value
pub const BigIntSpace = struct {
@@ -2485,7 +2497,6 @@ pub const Key = union(enum) {
return switch (storage) {
.big_int => |x| x,
inline .u64, .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
- .lazy_align, .lazy_size => unreachable,
};
}
};
@@ -2734,6 +2745,7 @@ pub const Key = union(enum) {
switch (namespace_type) {
.declared => |declared| {
std.hash.autoHash(&hasher, declared.zir_index);
+ std.hash.autoHash(&hasher, declared.arg_ty);
const captures = switch (declared.captures) {
.owned => |cvs| cvs.get(ip),
.external => |cvs| cvs,
@@ -2742,13 +2754,13 @@ pub const Key = union(enum) {
std.hash.autoHash(&hasher, cv);
}
},
- .generated_tag => |generated_tag| {
- std.hash.autoHash(&hasher, generated_tag.union_type);
- },
.reified => |reified| {
std.hash.autoHash(&hasher, reified.zir_index);
std.hash.autoHash(&hasher, reified.type_hash);
},
+ .generated_union_tag => |union_type| {
+ std.hash.autoHash(&hasher, union_type);
+ },
}
return hasher.final();
},
@@ -2756,23 +2768,12 @@ pub const Key = union(enum) {
.int => |int| {
var hasher = Hash.init(seed);
// Canonicalize all integers by converting them to BigIntConst.
- switch (int.storage) {
- .u64, .i64, .big_int => {
- var buffer: Key.Int.Storage.BigIntSpace = undefined;
- const big_int = int.storage.toBigInt(&buffer);
+ var buffer: Key.Int.Storage.BigIntSpace = undefined;
+ const big_int = int.storage.toBigInt(&buffer);
- std.hash.autoHash(&hasher, int.ty);
- std.hash.autoHash(&hasher, big_int.positive);
- for (big_int.limbs) |limb| std.hash.autoHash(&hasher, limb);
- },
- .lazy_align, .lazy_size => |lazy_ty| {
- std.hash.autoHash(
- &hasher,
- @as(@typeInfo(Key.Int.Storage).@"union".tag_type.?, int.storage),
- );
- std.hash.autoHash(&hasher, lazy_ty);
- },
- }
+ std.hash.autoHash(&hasher, int.ty);
+ std.hash.autoHash(&hasher, big_int.positive);
+ for (big_int.limbs) |limb| std.hash.autoHash(&hasher, limb);
return hasher.final();
},
@@ -3102,27 +3103,16 @@ pub const Key = union(enum) {
.u64 => |bb| aa == bb,
.i64 => |bb| aa == bb,
.big_int => |bb| bb.orderAgainstScalar(aa) == .eq,
- .lazy_align, .lazy_size => false,
},
.i64 => |aa| switch (b_info.storage) {
.u64 => |bb| aa == bb,
.i64 => |bb| aa == bb,
.big_int => |bb| bb.orderAgainstScalar(aa) == .eq,
- .lazy_align, .lazy_size => false,
},
.big_int => |aa| switch (b_info.storage) {
.u64 => |bb| aa.orderAgainstScalar(bb) == .eq,
.i64 => |bb| aa.orderAgainstScalar(bb) == .eq,
.big_int => |bb| aa.eql(bb),
- .lazy_align, .lazy_size => false,
- },
- .lazy_align => |aa| switch (b_info.storage) {
- .u64, .i64, .big_int, .lazy_size => false,
- .lazy_align => |bb| aa == bb,
- },
- .lazy_size => |aa| switch (b_info.storage) {
- .u64, .i64, .big_int, .lazy_align => false,
- .lazy_size => |bb| aa == bb,
},
};
},
@@ -3165,6 +3155,7 @@ pub const Key = union(enum) {
.declared => |a_d| {
const b_d = b_info.declared;
if (a_d.zir_index != b_d.zir_index) return false;
+ if (a_d.arg_ty != b_d.arg_ty) return false;
const a_captures = switch (a_d.captures) {
.owned => |s| s.get(ip),
.external => |cvs| cvs,
@@ -3175,12 +3166,12 @@ pub const Key = union(enum) {
};
return std.mem.eql(u32, @ptrCast(a_captures), @ptrCast(b_captures));
},
- .generated_tag => |a_gt| return a_gt.union_type == b_info.generated_tag.union_type,
.reified => |a_r| {
const b_r = b_info.reified;
return a_r.zir_index == b_r.zir_index and
a_r.type_hash == b_r.type_hash;
},
+ .generated_union_tag => |a_union_ty| return a_union_ty == b_info.generated_union_tag,
}
},
.aggregate => |a_info| {
@@ -3313,374 +3304,40 @@ pub const Key = union(enum) {
}
};
-pub const RequiresComptime = enum(u2) { no, yes, unknown, wip };
-
-// Unlike `Tag.TypeUnion` which is an encoding, and `Key.UnionType` which is a
-// minimal hashmap key, this type is a convenience type that contains info
-// needed by semantic analysis.
-pub const LoadedUnionType = struct {
- tid: Zcu.PerThread.Id,
- /// The index of the `Tag.TypeUnion` payload.
- extra_index: u32,
- // TODO: the non-fqn will be needed by the new dwarf structure
- /// The name of this union type.
- name: NullTerminatedString,
- /// Represents the declarations inside this union.
- namespace: NamespaceIndex,
- /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
- /// Otherwise, this is `.none`.
- name_nav: Nav.Index.Optional,
- /// The enum tag type.
- enum_tag_ty: Index,
- /// List of field types in declaration order.
- /// These are `none` until `status` is `have_field_types` or `have_layout`.
- field_types: Index.Slice,
- /// List of field alignments in declaration order.
- /// `none` means the ABI alignment of the type.
- /// If this slice has length 0 it means all elements are `none`.
- field_aligns: Alignment.Slice,
- /// Index of the union_decl or reify ZIR instruction.
- zir_index: TrackedInst.Index,
- captures: CaptureValue.Slice,
-
- pub const RuntimeTag = enum(u2) {
- none,
- safety,
- tagged,
-
- pub fn hasTag(self: RuntimeTag) bool {
- return switch (self) {
- .none => false,
- .tagged, .safety => true,
- };
- }
- };
-
- pub const Status = enum(u3) {
- none,
- field_types_wip,
- have_field_types,
- layout_wip,
- have_layout,
- fully_resolved_wip,
- /// The types and all its fields have had their layout resolved.
- /// Even through pointer, which `have_layout` does not ensure.
- fully_resolved,
-
- pub fn haveFieldTypes(status: Status) bool {
- return switch (status) {
- .none,
- .field_types_wip,
- => false,
- .have_field_types,
- .layout_wip,
- .have_layout,
- .fully_resolved_wip,
- .fully_resolved,
- => true,
- };
- }
-
- pub fn haveLayout(status: Status) bool {
- return switch (status) {
- .none,
- .field_types_wip,
- .have_field_types,
- .layout_wip,
- => false,
- .have_layout,
- .fully_resolved_wip,
- .fully_resolved,
- => true,
- };
- }
- };
-
- pub fn loadTagType(self: LoadedUnionType, ip: *const InternPool) LoadedEnumType {
- return ip.loadEnumType(self.enum_tag_ty);
- }
-
- /// Pointer to an enum type which is used for the tag of the union.
- /// This type is created even for untagged unions, even when the memory
- /// layout does not store the tag.
- /// Whether zig chooses this type or the user specifies it, it is stored here.
- /// This will be set to the null type until status is `have_field_types`.
- /// This accessor is provided so that the tag type can be mutated, and so that
- /// when it is mutated, the mutations are observed.
- /// The returned pointer expires with any addition to the `InternPool`.
- fn tagTypePtr(self: LoadedUnionType, ip: *const InternPool) *Index {
- const extra = ip.getLocalShared(self.tid).extra.acquire();
- const field_index = std.meta.fieldIndex(Tag.TypeUnion, "tag_ty").?;
- return @ptrCast(&extra.view().items(.@"0")[self.extra_index + field_index]);
- }
-
- pub fn tagTypeUnordered(u: LoadedUnionType, ip: *const InternPool) Index {
- return @atomicLoad(Index, u.tagTypePtr(ip), .unordered);
- }
-
- pub fn setTagType(u: LoadedUnionType, ip: *InternPool, io: Io, tag_type: Index) void {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- @atomicStore(Index, u.tagTypePtr(ip), tag_type, .release);
- }
-
- /// The returned pointer expires with any addition to the `InternPool`.
- fn flagsPtr(self: LoadedUnionType, ip: *const InternPool) *Tag.TypeUnion.Flags {
- const extra = ip.getLocalShared(self.tid).extra.acquire();
- const field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?;
- return @ptrCast(&extra.view().items(.@"0")[self.extra_index + field_index]);
- }
-
- pub fn flagsUnordered(u: LoadedUnionType, ip: *const InternPool) Tag.TypeUnion.Flags {
- return @atomicLoad(Tag.TypeUnion.Flags, u.flagsPtr(ip), .unordered);
- }
-
- pub fn setStatus(u: LoadedUnionType, ip: *InternPool, io: Io, status: Status) void {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.status = status;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- }
-
- pub fn setStatusIfLayoutWip(u: LoadedUnionType, ip: *InternPool, io: Io, status: Status) void {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- if (flags.status == .layout_wip) flags.status = status;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- }
-
- pub fn setAlignment(u: LoadedUnionType, ip: *InternPool, io: Io, alignment: Alignment) void {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.alignment = alignment;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- }
-
- pub fn assumeRuntimeBitsIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, io: Io) bool {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer if (flags.status == .field_types_wip) {
- flags.assumed_runtime_bits = true;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- };
- return flags.status == .field_types_wip;
- }
-
- pub fn requiresComptime(u: LoadedUnionType, ip: *const InternPool) RequiresComptime {
- return u.flagsUnordered(ip).requires_comptime;
- }
-
- pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool, io: Io) RequiresComptime {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer if (flags.requires_comptime == .unknown) {
- flags.requires_comptime = .wip;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- };
- return flags.requires_comptime;
- }
-
- pub fn setRequiresComptime(u: LoadedUnionType, ip: *InternPool, io: Io, requires_comptime: RequiresComptime) void {
- assert(requires_comptime != .wip); // see setRequiresComptimeWip
-
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.requires_comptime = requires_comptime;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- }
-
- pub fn assumePointerAlignedIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, io: Io, ptr_align: Alignment) bool {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer if (flags.status == .field_types_wip) {
- flags.alignment = ptr_align;
- flags.assumed_pointer_aligned = true;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- };
- return flags.status == .field_types_wip;
- }
-
- /// The returned pointer expires with any addition to the `InternPool`.
- fn sizePtr(self: LoadedUnionType, ip: *const InternPool) *u32 {
- const extra = ip.getLocalShared(self.tid).extra.acquire();
- const field_index = std.meta.fieldIndex(Tag.TypeUnion, "size").?;
- return &extra.view().items(.@"0")[self.extra_index + field_index];
- }
-
- pub fn sizeUnordered(u: LoadedUnionType, ip: *const InternPool) u32 {
- return @atomicLoad(u32, u.sizePtr(ip), .unordered);
- }
-
- /// The returned pointer expires with any addition to the `InternPool`.
- fn paddingPtr(self: LoadedUnionType, ip: *const InternPool) *u32 {
- const extra = ip.getLocalShared(self.tid).extra.acquire();
- const field_index = std.meta.fieldIndex(Tag.TypeUnion, "padding").?;
- return &extra.view().items(.@"0")[self.extra_index + field_index];
- }
-
- pub fn paddingUnordered(u: LoadedUnionType, ip: *const InternPool) u32 {
- return @atomicLoad(u32, u.paddingPtr(ip), .unordered);
- }
-
- pub fn hasTag(self: LoadedUnionType, ip: *const InternPool) bool {
- return self.flagsUnordered(ip).runtime_tag.hasTag();
- }
-
- pub fn haveFieldTypes(self: LoadedUnionType, ip: *const InternPool) bool {
- return self.flagsUnordered(ip).status.haveFieldTypes();
- }
-
- pub fn haveLayout(self: LoadedUnionType, ip: *const InternPool) bool {
- return self.flagsUnordered(ip).status.haveLayout();
- }
-
- pub fn setHaveLayout(u: LoadedUnionType, ip: *InternPool, io: Io, size: u32, padding: u32, alignment: Alignment) void {
- const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- @atomicStore(u32, u.sizePtr(ip), size, .unordered);
- @atomicStore(u32, u.paddingPtr(ip), padding, .unordered);
- const flags_ptr = u.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.alignment = alignment;
- flags.status = .have_layout;
- @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
- }
-
- pub fn fieldAlign(self: LoadedUnionType, ip: *const InternPool, field_index: usize) Alignment {
- if (self.field_aligns.len == 0) return .none;
- return self.field_aligns.get(ip)[field_index];
- }
-
- /// This does not mutate the field of LoadedUnionType.
- pub fn setZirIndex(self: LoadedUnionType, ip: *InternPool, new_zir_index: TrackedInst.Index.Optional) void {
- const flags_field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?;
- const zir_index_field_index = std.meta.fieldIndex(Tag.TypeUnion, "zir_index").?;
- const ptr: *TrackedInst.Index.Optional =
- @ptrCast(&ip.extra_.items[self.flags_index - flags_field_index + zir_index_field_index]);
- ptr.* = new_zir_index;
- }
-
- pub fn setFieldTypes(self: LoadedUnionType, ip: *const InternPool, types: []const Index) void {
- @memcpy(self.field_types.get(ip), types);
- }
-
- pub fn setFieldAligns(self: LoadedUnionType, ip: *const InternPool, aligns: []const Alignment) void {
- if (aligns.len == 0) return;
- assert(self.flagsUnordered(ip).any_aligned_fields);
- @memcpy(self.field_aligns.get(ip), aligns);
- }
-};
-
-pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
- const unwrapped_index = index.unwrap(ip);
- const extra_list = unwrapped_index.getExtra(ip);
- const data = unwrapped_index.getData(ip);
- const type_union = extraDataTrail(extra_list, Tag.TypeUnion, data);
- const fields_len = type_union.data.fields_len;
-
- var extra_index = type_union.end;
- const captures_len = if (type_union.data.flags.any_captures) c: {
- const len = extra_list.view().items(.@"0")[extra_index];
- extra_index += 1;
- break :c len;
- } else 0;
-
- const captures: CaptureValue.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = captures_len,
- };
- extra_index += captures_len;
- if (type_union.data.flags.is_reified) {
- extra_index += 2; // PackedU64
- }
-
- const field_types: Index.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
-
- const field_aligns = if (type_union.data.flags.any_aligned_fields) a: {
- const a: Alignment.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += std.math.divCeil(u32, fields_len, 4) catch unreachable;
- break :a a;
- } else Alignment.Slice.empty;
-
- return .{
- .tid = unwrapped_index.tid,
- .extra_index = data,
- .name = type_union.data.name,
- .name_nav = type_union.data.name_nav,
- .namespace = type_union.data.namespace,
- .enum_tag_ty = type_union.data.tag_ty,
- .field_types = field_types,
- .field_aligns = field_aligns,
- .zir_index = type_union.data.zir_index,
- .captures = captures,
- };
-}
-
pub const LoadedStructType = struct {
- tid: Zcu.PerThread.Id,
- /// The index of the `Tag.TypeStruct` or `Tag.TypeStructPacked` payload.
- extra_index: u32,
+ /// Index of the `struct_decl` or `reify` ZIR instruction.
+ zir_index: TrackedInst.Index,
+ captures: CaptureValue.Slice,
+
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this struct type.
name: NullTerminatedString,
- namespace: NamespaceIndex,
/// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
/// Otherwise, or if this is a file's root struct type, this is `.none`.
name_nav: Nav.Index.Optional,
- /// Index of the `struct_decl` or `reify` ZIR instruction.
- zir_index: TrackedInst.Index,
+ namespace: NamespaceIndex,
+
layout: std.builtin.Type.ContainerLayout,
+ /// May be `undefined` if `layout != .@"packed"`.
+ packed_backing_mode: PackedBackingMode,
+ /// May be `undefined` if `layout != .@"packed",
+ packed_backing_int_type: Index,
+
+ field_name_map: MapIndex,
field_names: NullTerminatedString.Slice,
field_types: Index.Slice,
- field_inits: Index.Slice,
+ field_defaults: Index.Slice,
field_aligns: Alignment.Slice,
- runtime_order: RuntimeOrder.Slice,
- comptime_bits: ComptimeBits,
- offsets: Offsets,
- names_map: OptionalMapIndex,
- captures: CaptureValue.Slice,
+ field_is_comptime_bits: ComptimeBits,
+ field_runtime_order: RuntimeOrder.Slice,
+ field_offsets: Offsets,
+
+ // These fields are only valid once the layout is resolved, and are never valid for `layout == .@"packed"`.
+ has_no_possible_value: bool,
+ has_one_possible_value: bool,
+ comptime_only: bool,
+ size: u32,
+ alignment: Alignment,
pub const ComptimeBits = struct {
tid: Zcu.PerThread.Id,
@@ -3690,22 +3347,14 @@ pub const LoadedStructType = struct {
pub const empty: ComptimeBits = .{ .tid = .main, .start = 0, .len = 0 };
- pub fn get(this: ComptimeBits, ip: *const InternPool) []u32 {
+ pub fn getAll(this: ComptimeBits, ip: *const InternPool) []u32 {
const extra = ip.getLocalShared(this.tid).extra.acquire();
return extra.view().items(.@"0")[this.start..][0..this.len];
}
- pub fn getBit(this: ComptimeBits, ip: *const InternPool, i: usize) bool {
+ pub fn get(this: ComptimeBits, ip: *const InternPool, i: usize) bool {
if (this.len == 0) return false;
- return @as(u1, @truncate(this.get(ip)[i / 32] >> @intCast(i % 32))) != 0;
- }
-
- pub fn setBit(this: ComptimeBits, ip: *const InternPool, i: usize) void {
- this.get(ip)[i / 32] |= @as(u32, 1) << @intCast(i % 32);
- }
-
- pub fn clearBit(this: ComptimeBits, ip: *const InternPool, i: usize) void {
- this.get(ip)[i / 32] &= ~(@as(u32, 1) << @intCast(i % 32));
+ return @as(u1, @truncate(this.getAll(ip)[i / 32] >> @intCast(i % 32))) != 0;
}
};
@@ -3753,865 +3402,550 @@ pub const LoadedStructType = struct {
/// Look up field index based on field name.
pub fn nameIndex(s: LoadedStructType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
- const names_map = s.names_map.unwrap() orelse {
- const i = name.toUnsigned(ip) orelse return null;
- if (i >= s.field_types.len) return null;
- return i;
- };
- const map = names_map.get(ip);
+ const map = s.field_name_map.get(ip);
const adapter: NullTerminatedString.Adapter = .{ .strings = s.field_names.get(ip) };
const field_index = map.getIndexAdapted(name, adapter) orelse return null;
return @intCast(field_index);
}
- /// Returns the already-existing field with the same name, if any.
- pub fn addFieldName(
- s: LoadedStructType,
- ip: *InternPool,
- name: NullTerminatedString,
- ) ?u32 {
- const extra = ip.getLocalShared(s.tid).extra.acquire();
- return ip.addFieldName(extra, s.names_map.unwrap().?, s.field_names.start, name);
- }
-
- pub fn fieldAlign(s: LoadedStructType, ip: *const InternPool, i: usize) Alignment {
- if (s.field_aligns.len == 0) return .none;
- return s.field_aligns.get(ip)[i];
- }
-
- pub fn fieldInit(s: LoadedStructType, ip: *const InternPool, i: usize) Index {
- if (s.field_inits.len == 0) return .none;
- assert(s.haveFieldInits(ip));
- return s.field_inits.get(ip)[i];
- }
-
- pub fn fieldName(s: LoadedStructType, ip: *const InternPool, i: usize) NullTerminatedString {
- return s.field_names.get(ip)[i];
- }
-
- pub fn fieldIsComptime(s: LoadedStructType, ip: *const InternPool, i: usize) bool {
- return s.comptime_bits.getBit(ip, i);
- }
-
- pub fn setFieldComptime(s: LoadedStructType, ip: *InternPool, i: usize) void {
- s.comptime_bits.setBit(ip, i);
- }
-
- /// The returned pointer expires with any addition to the `InternPool`.
- /// Asserts the struct is not packed.
- fn flagsPtr(s: LoadedStructType, ip: *const InternPool) *Tag.TypeStruct.Flags {
- assert(s.layout != .@"packed");
- const extra = ip.getLocalShared(s.tid).extra.acquire();
- const flags_field_index = std.meta.fieldIndex(Tag.TypeStruct, "flags").?;
- return @ptrCast(&extra.view().items(.@"0")[s.extra_index + flags_field_index]);
- }
-
- pub fn flagsUnordered(s: LoadedStructType, ip: *const InternPool) Tag.TypeStruct.Flags {
- return @atomicLoad(Tag.TypeStruct.Flags, s.flagsPtr(ip), .unordered);
- }
-
- /// The returned pointer expires with any addition to the `InternPool`.
- /// Asserts that the struct is packed.
- fn packedFlagsPtr(s: LoadedStructType, ip: *const InternPool) *Tag.TypeStructPacked.Flags {
- assert(s.layout == .@"packed");
- const extra = ip.getLocalShared(s.tid).extra.acquire();
- const flags_field_index = std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?;
- return @ptrCast(&extra.view().items(.@"0")[s.extra_index + flags_field_index]);
- }
-
- pub fn packedFlagsUnordered(s: LoadedStructType, ip: *const InternPool) Tag.TypeStructPacked.Flags {
- return @atomicLoad(Tag.TypeStructPacked.Flags, s.packedFlagsPtr(ip), .unordered);
- }
-
- /// Reads the non-opv flag calculated during AstGen. Used to short-circuit more
- /// complicated logic.
- pub fn knownNonOpv(s: LoadedStructType, ip: *const InternPool) bool {
- return switch (s.layout) {
- .@"packed" => false,
- .auto, .@"extern" => s.flagsUnordered(ip).known_non_opv,
- };
- }
-
- pub fn requiresComptime(s: LoadedStructType, ip: *const InternPool) RequiresComptime {
- return s.flagsUnordered(ip).requires_comptime;
- }
-
- pub fn setRequiresComptimeWip(s: LoadedStructType, ip: *InternPool, io: Io) RequiresComptime {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer if (flags.requires_comptime == .unknown) {
- flags.requires_comptime = .wip;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- };
- return flags.requires_comptime;
- }
-
- pub fn setRequiresComptime(s: LoadedStructType, ip: *InternPool, io: Io, requires_comptime: RequiresComptime) void {
- assert(requires_comptime != .wip); // see setRequiresComptimeWip
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.requires_comptime = requires_comptime;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- pub fn assumeRuntimeBitsIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
- if (s.layout == .@"packed") return false;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer if (flags.field_types_wip) {
- flags.assumed_runtime_bits = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- };
- return flags.field_types_wip;
- }
-
- pub fn setFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
- if (s.layout == .@"packed") return false;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer {
- flags.field_types_wip = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
- return flags.field_types_wip;
- }
-
- pub fn clearFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
- if (s.layout == .@"packed") return;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.field_types_wip = false;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- pub fn setLayoutWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
- if (s.layout == .@"packed") return false;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer {
- flags.layout_wip = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
- return flags.layout_wip;
- }
-
- pub fn clearLayoutWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
- if (s.layout == .@"packed") return;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.layout_wip = false;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- pub fn setAlignment(s: LoadedStructType, ip: *InternPool, io: Io, alignment: Alignment) void {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.alignment = alignment;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- pub fn assumePointerAlignedIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io, ptr_align: Alignment) bool {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer if (flags.field_types_wip) {
- flags.alignment = ptr_align;
- flags.assumed_pointer_aligned = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- };
- return flags.field_types_wip;
- }
-
- pub fn assumePointerAlignedIfWip(s: LoadedStructType, ip: *InternPool, io: Io, ptr_align: Alignment) bool {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer {
- if (flags.alignment_wip) {
- flags.alignment = ptr_align;
- flags.assumed_pointer_aligned = true;
- } else flags.alignment_wip = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
- return flags.alignment_wip;
- }
-
- pub fn clearAlignmentWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
- if (s.layout == .@"packed") return;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.alignment_wip = false;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- pub fn setInitsWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- switch (s.layout) {
- .@"packed" => {
- const flags_ptr = s.packedFlagsPtr(ip);
- var flags = flags_ptr.*;
- defer {
- flags.field_inits_wip = true;
- @atomicStore(Tag.TypeStructPacked.Flags, flags_ptr, flags, .release);
- }
- return flags.field_inits_wip;
- },
- .auto, .@"extern" => {
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer {
- flags.field_inits_wip = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
- return flags.field_inits_wip;
- },
- }
- }
-
- pub fn clearInitsWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- switch (s.layout) {
- .@"packed" => {
- const flags_ptr = s.packedFlagsPtr(ip);
- var flags = flags_ptr.*;
- flags.field_inits_wip = false;
- @atomicStore(Tag.TypeStructPacked.Flags, flags_ptr, flags, .release);
- },
- .auto, .@"extern" => {
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.field_inits_wip = false;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- },
- }
- }
-
- pub fn setFullyResolved(s: LoadedStructType, ip: *InternPool, io: Io) bool {
- if (s.layout == .@"packed") return true;
-
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- defer {
- flags.fully_resolved = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
- return flags.fully_resolved;
- }
-
- pub fn clearFullyResolved(s: LoadedStructType, ip: *InternPool, io: Io) void {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.fully_resolved = false;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- /// The returned pointer expires with any addition to the `InternPool`.
- /// Asserts the struct is not packed.
- fn sizePtr(s: LoadedStructType, ip: *const InternPool) *u32 {
- assert(s.layout != .@"packed");
- const extra = ip.getLocalShared(s.tid).extra.acquire();
- const size_field_index = std.meta.fieldIndex(Tag.TypeStruct, "size").?;
- return @ptrCast(&extra.view().items(.@"0")[s.extra_index + size_field_index]);
- }
-
- pub fn sizeUnordered(s: LoadedStructType, ip: *const InternPool) u32 {
- return @atomicLoad(u32, s.sizePtr(ip), .unordered);
- }
-
- /// The backing integer type of the packed struct. Whether zig chooses
- /// this type or the user specifies it, it is stored here. This will be
- /// set to `none` until the layout is resolved.
- /// Asserts the struct is packed.
- fn backingIntTypePtr(s: LoadedStructType, ip: *const InternPool) *Index {
- assert(s.layout == .@"packed");
- const extra = ip.getLocalShared(s.tid).extra.acquire();
- const field_index = std.meta.fieldIndex(Tag.TypeStructPacked, "backing_int_ty").?;
- return @ptrCast(&extra.view().items(.@"0")[s.extra_index + field_index]);
- }
-
- pub fn backingIntTypeUnordered(s: LoadedStructType, ip: *const InternPool) Index {
- return @atomicLoad(Index, s.backingIntTypePtr(ip), .unordered);
- }
-
- pub fn setBackingIntType(s: LoadedStructType, ip: *InternPool, io: Io, backing_int_ty: Index) void {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- @atomicStore(Index, s.backingIntTypePtr(ip), backing_int_ty, .release);
- }
-
- /// Asserts the struct is not packed.
- pub fn setZirIndex(s: LoadedStructType, ip: *InternPool, new_zir_index: TrackedInst.Index.Optional) void {
- assert(s.layout != .@"packed");
- const field_index = std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?;
- ip.extra_.items[s.extra_index + field_index] = @intFromEnum(new_zir_index);
- }
-
- pub fn haveFieldTypes(s: LoadedStructType, ip: *const InternPool) bool {
- const types = s.field_types.get(ip);
- return types.len == 0 or types[types.len - 1] != .none;
- }
-
- pub fn haveFieldInits(s: LoadedStructType, ip: *const InternPool) bool {
- return switch (s.layout) {
- .@"packed" => s.packedFlagsUnordered(ip).inits_resolved,
- .auto, .@"extern" => s.flagsUnordered(ip).inits_resolved,
- };
- }
-
- pub fn setHaveFieldInits(s: LoadedStructType, ip: *InternPool, io: Io) void {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- switch (s.layout) {
- .@"packed" => {
- const flags_ptr = s.packedFlagsPtr(ip);
- var flags = flags_ptr.*;
- flags.inits_resolved = true;
- @atomicStore(Tag.TypeStructPacked.Flags, flags_ptr, flags, .release);
- },
- .auto, .@"extern" => {
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.inits_resolved = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- },
- }
- }
-
- pub fn haveLayout(s: LoadedStructType, ip: *const InternPool) bool {
- return switch (s.layout) {
- .@"packed" => s.backingIntTypeUnordered(ip) != .none,
- .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved,
- };
- }
-
- pub fn setLayoutResolved(s: LoadedStructType, ip: *InternPool, io: Io, size: u32, alignment: Alignment) void {
- const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
- extra_mutex.lockUncancelable(io);
- defer extra_mutex.unlock(io);
-
- @atomicStore(u32, s.sizePtr(ip), size, .unordered);
- const flags_ptr = s.flagsPtr(ip);
- var flags = flags_ptr.*;
- flags.alignment = alignment;
- flags.layout_resolved = true;
- @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
- }
-
- pub fn hasReorderedFields(s: LoadedStructType) bool {
- return s.layout == .auto;
- }
-
- pub const RuntimeOrderIterator = struct {
- ip: *InternPool,
- field_index: u32,
- struct_type: InternPool.LoadedStructType,
-
- pub fn next(it: *@This()) ?u32 {
- var i = it.field_index;
-
- if (i >= it.struct_type.field_types.len)
- return null;
-
- if (it.struct_type.hasReorderedFields()) {
- it.field_index += 1;
- return it.struct_type.runtime_order.get(it.ip)[i].toInt();
- }
-
- while (it.struct_type.fieldIsComptime(it.ip, i)) {
- i += 1;
- if (i >= it.struct_type.field_types.len)
- return null;
- }
-
- it.field_index = i + 1;
- return i;
- }
- };
-
/// Iterates over non-comptime fields in the order they are laid out in memory at runtime.
/// May or may not include zero-bit fields.
/// Asserts the struct is not packed.
- pub fn iterateRuntimeOrder(s: LoadedStructType, ip: *InternPool) RuntimeOrderIterator {
- assert(s.layout != .@"packed");
- return .{
- .ip = ip,
- .field_index = 0,
- .struct_type = s,
- };
+ pub fn iterateRuntimeOrder(s: *const LoadedStructType, ip: *InternPool) RuntimeOrderIterator {
+ switch (s.layout) {
+ .auto => {
+ const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted);
+ return .{
+ .runtime_order = ro,
+ .fields_len = @intCast(ro.len),
+ .next_index = 0,
+ };
+ },
+ .@"extern" => return .{
+ .runtime_order = null,
+ .fields_len = s.field_names.len,
+ .next_index = 0,
+ },
+ .@"packed" => unreachable,
+ }
}
+ pub const RuntimeOrderIterator = struct {
+ runtime_order: ?[]const RuntimeOrder,
+ fields_len: u32,
+ next_index: u32,
+ pub fn next(it: *RuntimeOrderIterator) ?u32 {
+ const i = it.next_index;
+ if (i == it.fields_len) return null;
+ it.next_index = i + 1;
+ const ro = it.runtime_order orelse return i;
+ return ro[i].toInt().?;
+ }
+ };
+ pub fn iterateRuntimeOrderReverse(s: *const LoadedStructType, ip: *InternPool) ReverseRuntimeOrderIterator {
+ switch (s.layout) {
+ .auto => {
+ const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted);
+ return .{
+ .runtime_order = ro,
+ .last_index = @intCast(ro.len),
+ };
+ },
+ .@"extern" => return .{
+ .runtime_order = null,
+ .last_index = s.field_names.len,
+ },
+ .@"packed" => unreachable,
+ }
+ }
pub const ReverseRuntimeOrderIterator = struct {
- ip: *InternPool,
+ runtime_order: ?[]const RuntimeOrder,
last_index: u32,
- struct_type: InternPool.LoadedStructType,
-
- pub fn next(it: *@This()) ?u32 {
- if (it.last_index == 0)
- return null;
-
- if (it.struct_type.hasReorderedFields()) {
- it.last_index -= 1;
- const order = it.struct_type.runtime_order.get(it.ip);
- while (order[it.last_index] == .omitted) {
- it.last_index -= 1;
- if (it.last_index == 0)
- return null;
- }
- return order[it.last_index].toInt();
- }
-
- it.last_index -= 1;
- while (it.struct_type.fieldIsComptime(it.ip, it.last_index)) {
- it.last_index -= 1;
- if (it.last_index == 0)
- return null;
- }
-
- return it.last_index;
+ pub fn next(it: *ReverseRuntimeOrderIterator) ?u32 {
+ if (it.last_index == 0) return null;
+ const i = it.last_index - 1;
+ it.last_index = i;
+ const ro = it.runtime_order orelse return i;
+ return ro[i].toInt().?;
}
};
-
- pub fn iterateRuntimeOrderReverse(s: LoadedStructType, ip: *InternPool) ReverseRuntimeOrderIterator {
- assert(s.layout != .@"packed");
- return .{
- .ip = ip,
- .last_index = s.field_types.len,
- .struct_type = s,
- };
- }
};
-pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
- const unwrapped_index = index.unwrap(ip);
- const extra_list = unwrapped_index.getExtra(ip);
- const extra_items = extra_list.view().items(.@"0");
- const item = unwrapped_index.getItem(ip);
- switch (item.tag) {
- .type_struct => {
- const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name").?]);
- const name_nav: Nav.Index.Optional = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?]);
- const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?]);
- const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]);
- const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "fields_len").?];
- const flags: Tag.TypeStruct.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?], .unordered));
- var extra_index = item.data + @as(u32, @typeInfo(Tag.TypeStruct).@"struct".fields.len);
- const captures_len = if (flags.any_captures) c: {
- const len = extra_list.view().items(.@"0")[extra_index];
- extra_index += 1;
- break :c len;
- } else 0;
- const captures: CaptureValue.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = captures_len,
- };
- extra_index += captures_len;
- if (flags.is_reified) {
- extra_index += 2; // type_hash: PackedU64
- }
- const field_types: Index.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- const names_map: OptionalMapIndex, const names = n: {
- const names_map: OptionalMapIndex = @enumFromInt(extra_list.view().items(.@"0")[extra_index]);
- extra_index += 1;
- const names: NullTerminatedString.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- break :n .{ names_map, names };
- };
- const inits: Index.Slice = if (flags.any_default_inits) i: {
- const inits: Index.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- break :i inits;
- } else Index.Slice.empty;
- const aligns: Alignment.Slice = if (flags.any_aligned_fields) a: {
- const a: Alignment.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += std.math.divCeil(u32, fields_len, 4) catch unreachable;
- break :a a;
- } else Alignment.Slice.empty;
- const comptime_bits: LoadedStructType.ComptimeBits = if (flags.any_comptime_fields) c: {
- const len = std.math.divCeil(u32, fields_len, 32) catch unreachable;
- const c: LoadedStructType.ComptimeBits = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = len,
- };
- extra_index += len;
- break :c c;
- } else LoadedStructType.ComptimeBits.empty;
- const runtime_order: LoadedStructType.RuntimeOrder.Slice = if (!flags.is_extern) ro: {
- const ro: LoadedStructType.RuntimeOrder.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- break :ro ro;
- } else LoadedStructType.RuntimeOrder.Slice.empty;
- const offsets: LoadedStructType.Offsets = o: {
- const o: LoadedStructType.Offsets = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- break :o o;
- };
- return .{
- .tid = unwrapped_index.tid,
- .extra_index = item.data,
- .name = name,
- .name_nav = name_nav,
- .namespace = namespace,
- .zir_index = zir_index,
- .layout = if (flags.is_extern) .@"extern" else .auto,
- .field_names = names,
- .field_types = field_types,
- .field_inits = inits,
- .field_aligns = aligns,
- .runtime_order = runtime_order,
- .comptime_bits = comptime_bits,
- .offsets = offsets,
- .names_map = names_map,
- .captures = captures,
- };
- },
- .type_struct_packed, .type_struct_packed_inits => {
- const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?]);
- const name_nav: Nav.Index.Optional = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?]);
- const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]);
- const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "fields_len").?];
- const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?]);
- const names_map: MapIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "names_map").?]);
- const flags: Tag.TypeStructPacked.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?], .unordered));
- var extra_index = item.data + @as(u32, @typeInfo(Tag.TypeStructPacked).@"struct".fields.len);
- const has_inits = item.tag == .type_struct_packed_inits;
- const captures_len = if (flags.any_captures) c: {
- const len = extra_list.view().items(.@"0")[extra_index];
- extra_index += 1;
- break :c len;
- } else 0;
- const captures: CaptureValue.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = captures_len,
- };
- extra_index += captures_len;
- if (flags.is_reified) {
- extra_index += 2; // PackedU64
- }
- const field_types: Index.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- const field_names: NullTerminatedString.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- const field_inits: Index.Slice = if (has_inits) inits: {
- const i: Index.Slice = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = fields_len,
- };
- extra_index += fields_len;
- break :inits i;
- } else Index.Slice.empty;
- return .{
- .tid = unwrapped_index.tid,
- .extra_index = item.data,
- .name = name,
- .name_nav = name_nav,
- .namespace = namespace,
- .zir_index = zir_index,
- .layout = .@"packed",
- .field_names = field_names,
- .field_types = field_types,
- .field_inits = field_inits,
- .field_aligns = Alignment.Slice.empty,
- .runtime_order = LoadedStructType.RuntimeOrder.Slice.empty,
- .comptime_bits = LoadedStructType.ComptimeBits.empty,
- .offsets = LoadedStructType.Offsets.empty,
- .names_map = names_map.toOptional(),
- .captures = captures,
- };
- },
- else => unreachable,
- }
-}
+/// Unlike `Tag.TypeUnion` which is an encoding, and `Key.UnionType` which is a
+/// minimal hashmap key, this type is a convenience type that contains info
+/// needed by semantic analysis.
+pub const LoadedUnionType = struct {
+ /// Index of the `union_decl` or `reify` ZIR instruction.
+ zir_index: TrackedInst.Index,
+ captures: CaptureValue.Slice,
+
+ // TODO: the non-fqn will be needed by the new dwarf structure
+ /// The name of this union type.
+ name: NullTerminatedString,
+ /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
+ /// Otherwise, this is `.none`.
+ name_nav: Nav.Index.Optional,
+ namespace: NamespaceIndex,
+
+ layout: std.builtin.Type.ContainerLayout,
+ runtime_tag: RuntimeTag,
+ /// Even if `runtime_tag == .none`, this is populated with the union's "hypothetical" tag type.
+ enum_tag_type: Index,
+ /// May be `undefined` if `layout != .@"packed"`.
+ packed_backing_mode: PackedBackingMode,
+ /// May be `undefined` if `layout != .@"packed",
+ packed_backing_int_type: Index,
+
+ // Field names are not stored here, because fields are guaranteed to map one-to-one to the
+ // fields of the enum tag type. If you need field names, load them from `enum_tag_type`.
+ field_types: Index.Slice,
+ field_aligns: Alignment.Slice,
+
+ // These fields are only valid once the layout is resolved, and are never valid for `layout == .@"packed"`.
+ has_no_possible_value: bool,
+ has_one_possible_value: bool,
+ comptime_only: bool,
+ size: u32,
+ padding: u32,
+ alignment: Alignment,
+
+ pub const RuntimeTag = enum(u2) {
+ none,
+ safety,
+ tagged,
+ };
+};
pub const LoadedEnumType = struct {
+ /// This is `none` iff this is a generated tag type.
+ /// Otherwise, index of the `enum_decl` or `reify` ZIR instruction.
+ zir_index: TrackedInst.Index.Optional,
+ captures: CaptureValue.Slice,
+ /// If `zir_index` is `.none`, this is the union type for which this enum is the tag type.
+ owner_union: Index,
+
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this enum type.
name: NullTerminatedString,
- /// Represents the declarations inside this enum.
- namespace: NamespaceIndex,
/// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
/// Otherwise, this is `.none`.
name_nav: Nav.Index.Optional,
- /// An integer type which is used for the numerical value of the enum.
- /// This field is present regardless of whether the enum has an
- /// explicitly provided tag type or auto-numbered.
- tag_ty: Index,
- /// Set of field names in declaration order.
- names: NullTerminatedString.Slice,
- /// Maps integer tag value to field index.
- /// Entries are in declaration order, same as `fields`.
- /// If this is empty, it means the enum tags are auto-numbered.
- values: Index.Slice,
- tag_mode: TagMode,
- names_map: MapIndex,
- /// This is guaranteed to not be `.none` if explicit values are provided.
- values_map: OptionalMapIndex,
- /// This is `none` only if this is a generated tag type.
- zir_index: TrackedInst.Index.Optional,
- captures: CaptureValue.Slice,
+ namespace: NamespaceIndex,
- pub const TagMode = enum {
- /// The integer tag type was auto-numbered by zig.
- auto,
- /// The integer tag type was provided by the enum declaration, and the enum
- /// is exhaustive.
- explicit,
- /// The integer tag type was provided by the enum declaration, and the enum
- /// is non-exhaustive.
- nonexhaustive,
- };
+ /// An integer type which is used for the numerical value of the enum. Populated immediately, regardless
+ /// of whether the integer tag type was explicitly provided or inferred by the compiler.
+ int_tag_type: Index,
+ int_tag_is_explicit: bool,
+ nonexhaustive: bool,
+
+ /// Uses `NullTerminatedString.Adapter` with `field_names`.
+ field_name_map: MapIndex,
+ /// If this is `.none`, the enum tag type is auto-generated and so the fields are auto-numbered.
+ /// Otherwise, uses `Index.Adapter` with `field_values`.
+ field_value_map: OptionalMapIndex,
+ field_names: NullTerminatedString.Slice,
+ /// Empty if `field_value_map` is `.none`.
+ field_values: Index.Slice,
/// Look up field index based on field name.
- pub fn nameIndex(self: LoadedEnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
- const map = self.names_map.get(ip);
- const adapter: NullTerminatedString.Adapter = .{ .strings = self.names.get(ip) };
+ pub fn nameIndex(e: LoadedEnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
+ const map = e.field_name_map.get(ip);
+ const adapter: NullTerminatedString.Adapter = .{ .strings = e.field_names.get(ip) };
const field_index = map.getIndexAdapted(name, adapter) orelse return null;
return @intCast(field_index);
}
- /// Look up field index based on tag value.
- /// Asserts that `values_map` is not `none`.
- /// This function returns `null` when `tag_val` does not have the
- /// integer tag type of the enum.
- pub fn tagValueIndex(self: LoadedEnumType, ip: *const InternPool, tag_val: Index) ?u32 {
- assert(tag_val != .none);
- // TODO: we should probably decide a single interface for this function, but currently
- // it's being called with both tag values and underlying ints. Fix this!
- const int_tag_val = switch (ip.indexToKey(tag_val)) {
- .enum_tag => |enum_tag| enum_tag.int,
- .int => tag_val,
- else => unreachable,
- };
- if (self.values_map.unwrap()) |values_map| {
- const map = values_map.get(ip);
- const adapter: Index.Adapter = .{ .indexes = self.values.get(ip) };
- const field_index = map.getIndexAdapted(int_tag_val, adapter) orelse return null;
+ /// Look up field index based on integer tag value.
+ /// Asserts that the type of `tag_val` is `enum_obj.int_tag_type`.
+ /// Asserts that `tag_val` is not `undefined`.
+ pub fn tagValueIndex(e: LoadedEnumType, ip: *const InternPool, tag_val: Index) ?u32 {
+ assert(ip.typeOf(tag_val) == e.int_tag_type);
+ assert(ip.indexToKey(tag_val) == .int);
+ if (e.field_value_map.unwrap()) |field_value_map| {
+ const map = field_value_map.get(ip);
+ const adapter: Index.Adapter = .{ .indexes = e.field_values.get(ip) };
+ const field_index = map.getIndexAdapted(tag_val, adapter) orelse return null;
return @intCast(field_index);
}
- // Auto-numbered enum. Convert `int_tag_val` to field index.
- const field_index = switch (ip.indexToKey(int_tag_val).int.storage) {
+ // Auto-numbered enum, so convert `tag_val` to field index
+ const field_index = switch (ip.indexToKey(tag_val).int.storage) {
inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null,
.big_int => |x| x.toInt(u32) catch return null,
- .lazy_align, .lazy_size => unreachable,
};
- return if (field_index < self.names.len) field_index else null;
+ return if (field_index < e.field_names.len) field_index else null;
}
};
-pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
- const unwrapped_index = index.unwrap(ip);
- const extra_list = unwrapped_index.getExtra(ip);
- const item = unwrapped_index.getItem(ip);
- const tag_mode: LoadedEnumType.TagMode = switch (item.tag) {
- .type_enum_auto => {
- const extra = extraDataTrail(extra_list, EnumAuto, item.data);
- var extra_index: u32 = @intCast(extra.end);
- if (extra.data.zir_index == .none) {
- extra_index += 1; // owner_union
- }
- const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
- extra_index += 2; // type_hash: PackedU64
- break :c 0;
- } else extra.data.captures_len;
- return .{
- .name = extra.data.name,
- .name_nav = extra.data.name_nav,
- .namespace = extra.data.namespace,
- .tag_ty = extra.data.int_tag_type,
- .names = .{
- .tid = unwrapped_index.tid,
- .start = extra_index + captures_len,
- .len = extra.data.fields_len,
- },
- .values = Index.Slice.empty,
- .tag_mode = .auto,
- .names_map = extra.data.names_map,
- .values_map = .none,
- .zir_index = extra.data.zir_index,
- .captures = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = captures_len,
- },
- };
- },
- .type_enum_explicit => .explicit,
- .type_enum_nonexhaustive => .nonexhaustive,
- else => unreachable,
- };
- const extra = extraDataTrail(extra_list, EnumExplicit, item.data);
- var extra_index: u32 = @intCast(extra.end);
- if (extra.data.zir_index == .none) {
- extra_index += 1; // owner_union
- }
- const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
- extra_index += 2; // type_hash: PackedU64
- break :c 0;
- } else extra.data.captures_len;
- return .{
- .name = extra.data.name,
- .name_nav = extra.data.name_nav,
- .namespace = extra.data.namespace,
- .tag_ty = extra.data.int_tag_type,
- .names = .{
- .tid = unwrapped_index.tid,
- .start = extra_index + captures_len,
- .len = extra.data.fields_len,
- },
- .values = .{
- .tid = unwrapped_index.tid,
- .start = extra_index + captures_len + extra.data.fields_len,
- .len = if (extra.data.values_map != .none) extra.data.fields_len else 0,
- },
- .tag_mode = tag_mode,
- .names_map = extra.data.names_map,
- .values_map = extra.data.values_map,
- .zir_index = extra.data.zir_index,
- .captures = .{
- .tid = unwrapped_index.tid,
- .start = extra_index,
- .len = captures_len,
- },
- };
-}
-
-/// Note that this type doubles as the payload for `Tag.type_opaque`.
pub const LoadedOpaqueType = struct {
- /// Contains the declarations inside this opaque.
- namespace: NamespaceIndex,
+ /// Index of the `opaque_decl` instruction.
+ zir_index: TrackedInst.Index,
+ captures: CaptureValue.Slice,
+
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this opaque type.
name: NullTerminatedString,
/// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
/// Otherwise, this is `.none`.
name_nav: Nav.Index.Optional,
- /// Index of the `opaque_decl` or `reify` instruction.
- zir_index: TrackedInst.Index,
- captures: CaptureValue.Slice,
+ namespace: NamespaceIndex,
};
+pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
+ const unwrapped_index = index.unwrap(ip);
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra_items = extra_list.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ // Exiting this `switch` means this is a `packed struct`.
+ const backing_mode: PackedBackingMode, const any_defaults: bool = switch (item.tag) {
+ .type_struct_packed_auto => .{ .auto, false },
+ .type_struct_packed_explicit => .{ .explicit, false },
+ .type_struct_packed_auto_defaults => .{ .auto, true },
+ .type_struct_packed_explicit_defaults => .{ .explicit, true },
+ .type_struct => {
+ const extra = extraDataTrail(extra_list, Tag.TypeStruct, item.data);
+ var extra_index = extra.end;
+ const captures: CaptureValue.Slice = switch (extra.data.flags.any_captures) {
+ .reified => captures: {
+ extra_index += 2; // type_hash: PackedU64
+ break :captures .empty;
+ },
+ .false => .empty,
+ .true => captures: {
+ const len = extra_items[extra_index];
+ extra_index += 1;
+ break :captures .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = len,
+ };
+ },
+ };
+ extra_index += captures.len;
+ const field_names: NullTerminatedString.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_names.len;
+ const field_types: Index.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_types.len;
+ const field_defaults: Index.Slice = if (extra.data.flags.any_field_defaults) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ } else .empty;
+ extra_index += field_defaults.len;
+ const field_aligns: Alignment.Slice = if (extra.data.flags.any_field_aligns) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ } else .empty;
+ extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;
+ const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = std.math.divCeil(u32, extra.data.fields_len, 32) catch unreachable,
+ } else .empty;
+ extra_index += field_is_comptime_bits.len;
+ const field_runtime_order: LoadedStructType.RuntimeOrder.Slice = if (extra.data.flags.layout == .auto) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ } else .empty;
+ extra_index += field_runtime_order.len;
+ const field_offsets: LoadedStructType.Offsets = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_offsets.len;
+
+ return .{
+ .zir_index = extra.data.zir_index,
+ .captures = captures,
+ .name = extra.data.name,
+ .name_nav = extra.data.name_nav,
+ .namespace = extra.data.namespace,
+ .layout = switch (extra.data.flags.layout) {
+ .auto => .auto,
+ .@"extern" => .@"extern",
+ },
+ .packed_backing_mode = undefined,
+ .packed_backing_int_type = undefined,
+ .field_name_map = extra.data.field_name_map,
+ .field_names = field_names,
+ .field_types = field_types,
+ .field_defaults = field_defaults,
+ .field_aligns = field_aligns,
+ .field_is_comptime_bits = field_is_comptime_bits,
+ .field_runtime_order = field_runtime_order,
+ .field_offsets = field_offsets,
+ .has_no_possible_value = extra.data.flags.has_no_possible_value,
+ .has_one_possible_value = extra.data.flags.has_one_possible_value,
+ .comptime_only = extra.data.flags.comptime_only,
+ .size = extra.data.size,
+ .alignment = extra.data.flags.alignment,
+ };
+ },
+ else => unreachable,
+ };
+ const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, item.data);
+ var extra_index = extra.end;
+ const captures: CaptureValue.Slice = switch (extra.data.captures_len) {
+ .reified => captures: {
+ extra_index += 2; // type_hash: PackedU64
+ break :captures .empty;
+ },
+ _ => .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = @intFromEnum(extra.data.captures_len),
+ },
+ };
+ extra_index += captures.len;
+ const field_names: NullTerminatedString.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_names.len;
+ const field_types: Index.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_types.len;
+ const field_defaults: Index.Slice = if (any_defaults) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ } else .empty;
+ extra_index += field_defaults.len;
+ return .{
+ .zir_index = extra.data.zir_index,
+ .captures = captures,
+ .name = extra.data.name,
+ .name_nav = extra.data.name_nav,
+ .namespace = extra.data.namespace,
+ .layout = .@"packed",
+ .packed_backing_mode = backing_mode,
+ .packed_backing_int_type = extra.data.backing_int_type,
+ .field_name_map = extra.data.field_name_map,
+ .field_names = field_names,
+ .field_types = field_types,
+ .field_defaults = field_defaults,
+ .field_aligns = .empty,
+ .field_is_comptime_bits = .empty,
+ .field_runtime_order = .empty,
+ .field_offsets = .empty,
+ .has_no_possible_value = undefined,
+ .has_one_possible_value = undefined,
+ .comptime_only = undefined,
+ .size = undefined,
+ .alignment = undefined,
+ };
+}
+
+pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
+ const unwrapped_index = index.unwrap(ip);
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra_items = extra_list.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ // Exiting this `switch` means this is a `packed union`.
+ const backing_mode: PackedBackingMode = switch (item.tag) {
+ .type_union_packed_auto => .auto,
+ .type_union_packed_explicit => .explicit,
+ .type_union => {
+ const extra = extraDataTrail(extra_list, Tag.TypeUnion, item.data);
+ var extra_index = extra.end;
+ const captures: CaptureValue.Slice = switch (extra.data.flags.any_captures) {
+ .reified => captures: {
+ extra_index += 2; // type_hash: PackedU64
+ break :captures .empty;
+ },
+ .false => .empty,
+ .true => captures: {
+ const len = extra_items[extra_index];
+ extra_index += 1;
+ break :captures .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = len,
+ };
+ },
+ };
+ extra_index += captures.len;
+ const field_types: Index.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_types.len;
+ const field_aligns: Alignment.Slice = if (extra.data.flags.any_field_aligns) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ } else .empty;
+ extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;
+
+ return .{
+ .zir_index = extra.data.zir_index,
+ .captures = captures,
+ .name = extra.data.name,
+ .name_nav = extra.data.name_nav,
+ .namespace = extra.data.namespace,
+ .layout = switch (extra.data.flags.layout) {
+ .auto => .auto,
+ .@"extern" => .@"extern",
+ },
+ .runtime_tag = extra.data.flags.runtime_tag,
+ .enum_tag_type = extra.data.enum_tag_type,
+ .packed_backing_mode = undefined,
+ .packed_backing_int_type = undefined,
+ .field_types = field_types,
+ .field_aligns = field_aligns,
+ .has_no_possible_value = extra.data.flags.has_no_possible_value,
+ .has_one_possible_value = extra.data.flags.has_one_possible_value,
+ .comptime_only = extra.data.flags.comptime_only,
+ .size = extra.data.size,
+ .padding = extra.data.padding,
+ .alignment = extra.data.flags.alignment,
+ };
+ },
+ else => unreachable,
+ };
+ const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, item.data);
+ var extra_index = extra.end;
+ const captures: CaptureValue.Slice = switch (extra.data.captures_len) {
+ .reified => captures: {
+ extra_index += 2; // type_hash: PackedU64
+ break :captures .empty;
+ },
+ _ => .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = @intFromEnum(extra.data.captures_len),
+ },
+ };
+ extra_index += captures.len;
+ const field_types: Index.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_types.len;
+ return .{
+ .zir_index = extra.data.zir_index,
+ .captures = captures,
+ .name = extra.data.name,
+ .name_nav = extra.data.name_nav,
+ .namespace = extra.data.namespace,
+ .layout = .@"packed",
+ .runtime_tag = .none,
+ .enum_tag_type = extra.data.enum_tag_type,
+ .packed_backing_mode = backing_mode,
+ .packed_backing_int_type = extra.data.backing_int_type,
+ .field_types = field_types,
+ .field_aligns = .empty,
+ .has_no_possible_value = undefined,
+ .has_one_possible_value = undefined,
+ .comptime_only = undefined,
+ .size = undefined,
+ .padding = undefined,
+ .alignment = undefined,
+ };
+}
+
+pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
+ const unwrapped_index = index.unwrap(ip);
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra_items = extra_list.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ const explicit_int_tag: bool, const nonexhaustive: bool = switch (item.tag) {
+ .type_enum_auto => .{ false, false },
+ .type_enum_explicit => .{ true, false },
+ .type_enum_nonexhaustive => .{ true, true },
+ else => unreachable,
+ };
+ const extra = extraDataTrail(extra_list, Tag.TypeEnum, item.data);
+ var extra_index: u32 = @intCast(extra.end);
+ const zir_index: TrackedInst.Index.Optional, const captures: CaptureValue.Slice, const owner_union: Index = switch (extra.data.captures_len) {
+ .reified => info: {
+ const zir_index: TrackedInst.Index = @enumFromInt(extra_items[extra_index]);
+ extra_index += 1;
+ extra_index += 2; // type_hash: PackedU64
+ break :info .{ zir_index.toOptional(), .empty, .none };
+ },
+ .generated_union_tag => info: {
+ const owner_union: Index = @enumFromInt(extra_items[extra_index]);
+ extra_index += 1;
+ break :info .{ .none, .empty, owner_union };
+ },
+ _ => info: {
+ const zir_index: TrackedInst.Index = @enumFromInt(extra_items[extra_index]);
+ extra_index += 1;
+ const captures: CaptureValue.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = @intFromEnum(extra.data.captures_len),
+ };
+ extra_index += captures.len;
+ break :info .{ zir_index.toOptional(), captures, .none };
+ },
+ };
+ const field_value_map: OptionalMapIndex = if (explicit_int_tag) m: {
+ const map: MapIndex = @enumFromInt(extra_items[extra_index]);
+ extra_index += 1;
+ break :m map.toOptional();
+ } else .none;
+ const field_names: NullTerminatedString.Slice = .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ };
+ extra_index += field_names.len;
+ const field_values: Index.Slice = if (explicit_int_tag) .{
+ .tid = unwrapped_index.tid,
+ .start = extra_index,
+ .len = extra.data.fields_len,
+ } else .empty;
+ extra_index += field_values.len;
+ return .{
+ .zir_index = zir_index,
+ .captures = captures,
+ .owner_union = owner_union,
+ .name = extra.data.name,
+ .name_nav = extra.data.name_nav,
+ .namespace = extra.data.namespace,
+ .int_tag_type = extra.data.int_tag_type,
+ .int_tag_is_explicit = explicit_int_tag,
+ .nonexhaustive = nonexhaustive,
+ .field_name_map = extra.data.field_name_map,
+ .field_value_map = field_value_map,
+ .field_names = field_names,
+ .field_values = field_values,
+ };
+}
+
pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType {
const unwrapped_index = index.unwrap(ip);
const item = unwrapped_index.getItem(ip);
assert(item.tag == .type_opaque);
const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, item.data);
- const captures_len = if (extra.data.captures_len == std.math.maxInt(u32))
- 0
- else
- extra.data.captures_len;
return .{
- .name = extra.data.name,
- .name_nav = extra.data.name_nav,
- .namespace = extra.data.namespace,
.zir_index = extra.data.zir_index,
.captures = .{
.tid = unwrapped_index.tid,
.start = extra.end,
- .len = captures_len,
+ .len = extra.data.captures_len,
},
+ .name = extra.data.name,
+ .name_nav = extra.data.name_nav,
+ .namespace = extra.data.namespace,
};
}
@@ -4819,7 +4153,7 @@ pub const Index = enum(u32) {
};
/// Used for a map of `Index` values to the index within a list of `Index` values.
- const Adapter = struct {
+ pub const Adapter = struct {
indexes: []const Index,
pub fn eql(ctx: @This(), a: Index, b_void: void, b_map_index: usize) bool {
@@ -4891,26 +4225,6 @@ pub const Index = enum(u32) {
/// Tag to encoding mapping to facilitate fancy debug printing for this type.
fn dbHelper(self: *Index, tag_to_encoding_map: *struct {
const DataIsIndex = struct { data: Index };
- const DataIsExtraIndexOfEnumExplicit = struct {
- const @"data.fields_len" = opaque {};
- data: *EnumExplicit,
- @"trailing.names.len": *@"data.fields_len",
- @"trailing.values.len": *@"data.fields_len",
- trailing: struct {
- names: []NullTerminatedString,
- values: []Index,
- },
- };
- const DataIsExtraIndexOfTypeTuple = struct {
- const @"data.fields_len" = opaque {};
- data: *TypeTuple,
- @"trailing.types.len": *@"data.fields_len",
- @"trailing.values.len": *@"data.fields_len",
- trailing: struct {
- types: []Index,
- values: []Index,
- },
- };
removed: void,
type_int_signed: struct { data: u32 },
@@ -4931,21 +4245,7 @@ pub const Index = enum(u32) {
trailing: struct { names: []NullTerminatedString },
},
type_inferred_error_set: DataIsIndex,
- type_enum_auto: struct {
- const @"data.fields_len" = opaque {};
- data: *EnumAuto,
- @"trailing.names.len": *@"data.fields_len",
- trailing: struct { names: []NullTerminatedString },
- },
- type_enum_explicit: DataIsExtraIndexOfEnumExplicit,
- type_enum_nonexhaustive: DataIsExtraIndexOfEnumExplicit,
simple_type: void,
- type_opaque: struct { data: *Tag.TypeOpaque },
- type_struct: struct { data: *Tag.TypeStruct },
- type_struct_packed: struct { data: *Tag.TypeStructPacked },
- type_struct_packed_inits: struct { data: *Tag.TypeStructPacked },
- type_tuple: DataIsExtraIndexOfTypeTuple,
- type_union: struct { data: *Tag.TypeUnion },
type_function: struct {
const @"data.flags.has_comptime_bits" = opaque {};
const @"data.flags.has_noalias_bits" = opaque {};
@@ -4956,6 +4256,29 @@ pub const Index = enum(u32) {
@"trailing.param_types.len": *@"data.params_len",
trailing: struct { comptime_bits: []u32, noalias_bits: []u32, param_types: []Index },
},
+ type_tuple: struct {
+ const @"data.fields_len" = opaque {};
+ data: *TypeTuple,
+ @"trailing.types.len": *@"data.fields_len",
+ @"trailing.values.len": *@"data.fields_len",
+ trailing: struct {
+ types: []Index,
+ values: []Index,
+ },
+ },
+
+ type_struct: struct { data: *Tag.TypeStruct },
+ type_struct_packed_auto: struct { data: *Tag.TypeStructPacked },
+ type_struct_packed_explicit: struct { data: *Tag.TypeStructPacked },
+ type_struct_packed_auto_defaults: struct { data: *Tag.TypeStructPacked },
+ type_struct_packed_explicit_defaults: struct { data: *Tag.TypeStructPacked },
+ type_union: struct { data: *Tag.TypeUnion },
+ type_union_packed_auto: struct { data: *Tag.TypeUnionPacked },
+ type_union_packed_explicit: struct { data: *Tag.TypeUnionPacked },
+ type_enum_auto: struct { data: *Tag.TypeEnum },
+ type_enum_explicit: struct { data: *Tag.TypeEnum },
+ type_enum_nonexhaustive: struct { data: *Tag.TypeEnum },
+ type_opaque: struct { data: *Tag.TypeOpaque },
undef: DataIsIndex,
simple_value: void,
@@ -4982,8 +4305,6 @@ pub const Index = enum(u32) {
int_small: struct { data: *IntSmall },
int_positive: struct { data: u32 },
int_negative: struct { data: u32 },
- int_lazy_align: struct { data: *IntLazy },
- int_lazy_size: struct { data: *IntLazy },
error_set_error: struct { data: *Key.Error },
error_union_error: struct { data: *Key.Error },
error_union_payload: struct { data: *Tag.TypeValue },
@@ -5485,6 +4806,8 @@ pub const Tag = enum(u8) {
/// assert not this tag. `data` is unused.
removed,
+ /// A type that can be represented with only an enum tag.
+ simple_type,
/// An integer type.
/// data is number of bits
type_int_signed,
@@ -5524,41 +4847,68 @@ pub const Tag = enum(u8) {
/// The inferred error set type of a function.
/// data is `Index` of a `func_decl` or `func_instance`.
type_inferred_error_set,
- /// An enum type with auto-numbered tag values.
- /// The enum is exhaustive.
- /// data is payload index to `EnumAuto`.
+ /// A function body type.
+ /// `data` is extra index to `TypeFunction`.
+ type_function,
+ /// A `TupleType`.
+ /// data is extra index of `TypeTuple`.
+ type_tuple,
+
+ /// A non-packed struct type.
+ /// data is extra index of `TypeStruct`.
+ type_struct,
+ /// `packed struct { ... }` with no default field values.
+ /// data is extra index of `TypeStructPacked`.
+ type_struct_packed_auto,
+ /// `packed struct(T) { ... }` with no default field values.
+ /// data is extra index of `TypeStructPacked`.
+ type_struct_packed_explicit,
+ /// `packed struct { ... }` with one or more default field values.
+ /// data is extra index of `TypeStructPacked`.
+ type_struct_packed_auto_defaults,
+ /// `packed struct(T) { ... }` with one or more default field values.
+ /// data is extra index of `TypeStructPacked`.
+ type_struct_packed_explicit_defaults,
+
+ /// A non-packed union type.
+ /// data is extra index of `TypeUnion`.
+ type_union,
+ /// `packed union { ... }`.
+ /// data is extra index of `TypeUnionPacked`.
+ type_union_packed_auto,
+ /// `packed union(T) { ... }`.
+ /// data is extra index of `TypeUnionPacked`.
+ type_union_packed_explicit,
+
+ /// An exhaustive enum type *without* an explicit integer tag type. The tag type is inferred.
+ ///
+ /// Because the tag type is inferred, there are no explicit field values.
+ ///
+ /// May be the generated tag type for a `union(enum)`.
+ ///
+ /// data is extra index of `TypeEnum`.
type_enum_auto,
- /// An enum type with an explicitly provided integer tag type.
- /// The enum is exhaustive.
- /// data is payload index to `EnumExplicit`.
+ /// An exhaustive enum type *with* an explicit integer tag type.
+ ///
+ /// May have explicit field values.
+ ///
+ /// May be the generated tag type for a `union(enum(T))`.
+ ///
+ /// data is extra index of `TypeEnum`.
type_enum_explicit,
- /// An enum type with an explicitly provided integer tag type.
- /// The enum is non-exhaustive.
- /// data is payload index to `EnumExplicit`.
+ /// An non-exhaustive enum type (with an explicit integer tag type, since it is required for
+ /// non-exhaustive enums).
+ ///
+ /// May have explicit field values.
+ ///
+ /// This is *not* a union's generated tag type, because such types are always exhaustive.
+ ///
+ /// data is extra index of `TypeEnum`.
type_enum_nonexhaustive,
- /// A type that can be represented with only an enum tag.
- simple_type,
+
/// An opaque type.
- /// data is index of Tag.TypeOpaque in extra.
+ /// data is extra index of `TypeOpaque`.
type_opaque,
- /// A non-packed struct type.
- /// data is 0 or extra index of `TypeStruct`.
- type_struct,
- /// A packed struct, no fields have any init values.
- /// data is extra index of `TypeStructPacked`.
- type_struct_packed,
- /// A packed struct, one or more fields have init values.
- /// data is extra index of `TypeStructPacked`.
- type_struct_packed_inits,
- /// A `TupleType`.
- /// data is extra index of `TypeTuple`.
- type_tuple,
- /// A union type.
- /// `data` is extra index of `TypeUnion`.
- type_union,
- /// A function body type.
- /// `data` is extra index to `TypeFunction`.
- type_function,
/// Typed `undefined`.
/// `data` is `Index` of the type.
@@ -5644,12 +4994,6 @@ pub const Tag = enum(u8) {
/// A negative integer value.
/// data is a limbs index to `Int`.
int_negative,
- /// The ABI alignment of a lazy type.
- /// data is extra index of `IntLazy`.
- int_lazy_align,
- /// The ABI size of a lazy type.
- /// data is extra index of `IntLazy`.
- int_lazy_size,
/// An error value.
/// data is extra index of `Key.Error`.
error_set_error,
@@ -5747,24 +5091,77 @@ pub const Tag = enum(u8) {
const Union = Key.Union;
const TypePointer = Key.PtrType;
+ const struct_packed_encoding = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeStructPacked,
+ .trailing = struct {
+ type_hash: ?u64,
+ captures: ?[]CaptureValue,
+ field_names: []NullTerminatedString,
+ field_types: []Index,
+ },
+ .config = .{
+ .@"trailing.type_hash.?" = .@"payload.captures_len == .reified",
+ .@"trailing.captures.?" = .@"payload.captures_len != .reified",
+ .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)",
+ .@"trailing.field_names.len" = .@"payload.fields_len",
+ .@"trailing.field_types.len" = .@"payload.fields_len",
+ },
+ };
+ const struct_packed_defaults_encoding = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeStructPacked,
+ .trailing = struct {
+ type_hash: ?u64,
+ captures: ?[]CaptureValue,
+ field_names: []NullTerminatedString,
+ field_types: []Index,
+ field_defaults: []Index,
+ },
+ .config = .{
+ .@"trailing.type_hash.?" = .@"payload.captures_len == .reified",
+ .@"trailing.captures.?" = .@"payload.captures_len != .reified",
+ .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)",
+ .@"trailing.field_names.len" = .@"payload.fields_len",
+ .@"trailing.field_types.len" = .@"payload.fields_len",
+ .@"trailing.field_defaults.len" = .@"payload.fields_len",
+ },
+ };
+ const union_packed_encoding = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeUnionPacked,
+ .trailing = struct {
+ type_hash: ?u64,
+ captures: ?[]CaptureValue,
+ field_types: []Index,
+ },
+ .config = .{
+ .@"trailing.type_hash.?" = .@"payload.captures_len == .reified",
+ .@"trailing.captures.?" = .@"payload.captures_len != .reified",
+ .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)",
+ .@"trailing.field_types.len" = .@"payload.fields_len",
+ },
+ };
const enum_explicit_encoding = .{
.summary = .@"{.payload.name%summary#\"}",
- .payload = EnumExplicit,
+ .payload = TypeEnum,
.trailing = struct {
- owner_union: Index,
- captures: ?[]CaptureValue,
+ owner_union: ?Index,
+ zir_index: ?TrackedInst.Index,
type_hash: ?u64,
+ captures: ?[]CaptureValue,
+ field_value_map: MapIndex,
field_names: []NullTerminatedString,
- tag_values: []Index,
+ field_values: []Index,
},
.config = .{
- .@"trailing.owner_union.?" = .@"payload.zir_index == .none",
- .@"trailing.cau.?" = .@"payload.zir_index != .none",
- .@"trailing.captures.?" = .@"payload.captures_len < 0xffffffff",
- .@"trailing.captures.?.len" = .@"payload.captures_len",
- .@"trailing.type_hash.?" = .@"payload.captures_len == 0xffffffff",
+ .@"trailing.owner_union.?" = .@"payload.captures_len == .generated_union_tag",
+ .@"trailing.zir_index.?" = .@"payload.captures_len != .generated_union_tag",
+ .@"trailing.type_hash.?" = .@"payload.captures_len == .reified",
+ .@"trailing.captures.?" = .@"payload.captures_len != .reified and payload.captures_len != .generated_enum_tag",
+ .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)",
.@"trailing.field_names.len" = .@"payload.fields_len",
- .@"trailing.tag_values.len" = .@"payload.fields_len",
+ .@"trailing.field_values.len" = .@"payload.fields_len",
},
};
const encodings = .{
@@ -5792,107 +5189,7 @@ pub const Tag = enum(u8) {
.summary = .@"@typeInfo(@typeInfo(@TypeOf({.data%summary})).@\"fn\".return_type.?).error_union.error_set",
.data = Index,
},
- .type_enum_auto = .{
- .summary = .@"{.payload.name%summary#\"}",
- .payload = EnumAuto,
- .trailing = struct {
- owner_union: ?Index,
- captures: ?[]CaptureValue,
- type_hash: ?u64,
- field_names: []NullTerminatedString,
- },
- .config = .{
- .@"trailing.owner_union.?" = .@"payload.zir_index == .none",
- .@"trailing.cau.?" = .@"payload.zir_index != .none",
- .@"trailing.captures.?" = .@"payload.captures_len < 0xffffffff",
- .@"trailing.captures.?.len" = .@"payload.captures_len",
- .@"trailing.type_hash.?" = .@"payload.captures_len == 0xffffffff",
- .@"trailing.field_names.len" = .@"payload.fields_len",
- },
- },
- .type_enum_explicit = enum_explicit_encoding,
- .type_enum_nonexhaustive = enum_explicit_encoding,
.simple_type = .{ .summary = .@"{.index%value#.}", .index = SimpleType },
- .type_opaque = .{
- .summary = .@"{.payload.name%summary#\"}",
- .payload = TypeOpaque,
- .trailing = struct { captures: []CaptureValue },
- .config = .{ .@"trailing.captures.len" = .@"payload.captures_len" },
- },
- .type_struct = .{
- .summary = .@"{.payload.name%summary#\"}",
- .payload = TypeStruct,
- .trailing = struct {
- captures_len: ?u32,
- captures: ?[]CaptureValue,
- type_hash: ?u64,
- field_types: []Index,
- field_names_map: OptionalMapIndex,
- field_names: []NullTerminatedString,
- field_inits: ?[]Index,
- field_aligns: ?[]Alignment,
- field_is_comptime_bits: ?[]u32,
- field_index: ?[]LoadedStructType.RuntimeOrder,
- field_offset: []u32,
- },
- .config = .{
- .@"trailing.captures_len.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
- .@"trailing.type_hash.?" = .@"payload.flags.is_reified",
- .@"trailing.field_types.len" = .@"payload.fields_len",
- .@"trailing.field_names.len" = .@"payload.fields_len",
- .@"trailing.field_inits.?" = .@"payload.flags.any_default_inits",
- .@"trailing.field_inits.?.len" = .@"payload.fields_len",
- .@"trailing.field_aligns.?" = .@"payload.flags.any_aligned_fields",
- .@"trailing.field_aligns.?.len" = .@"payload.fields_len",
- .@"trailing.field_is_comptime_bits.?" = .@"payload.flags.any_comptime_fields",
- .@"trailing.field_is_comptime_bits.?.len" = .@"(payload.fields_len + 31) / 32",
- .@"trailing.field_index.?" = .@"!payload.flags.is_extern",
- .@"trailing.field_index.?.len" = .@"payload.fields_len",
- .@"trailing.field_offset.len" = .@"payload.fields_len",
- },
- },
- .type_struct_packed = .{
- .summary = .@"{.payload.name%summary#\"}",
- .payload = TypeStructPacked,
- .trailing = struct {
- captures_len: ?u32,
- captures: ?[]CaptureValue,
- type_hash: ?u64,
- field_types: []Index,
- field_names: []NullTerminatedString,
- },
- .config = .{
- .@"trailing.captures_len.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
- .@"trailing.type_hash.?" = .@"payload.is_flags.is_reified",
- .@"trailing.field_types.len" = .@"payload.fields_len",
- .@"trailing.field_names.len" = .@"payload.fields_len",
- },
- },
- .type_struct_packed_inits = .{
- .summary = .@"{.payload.name%summary#\"}",
- .payload = TypeStructPacked,
- .trailing = struct {
- captures_len: ?u32,
- captures: ?[]CaptureValue,
- type_hash: ?u64,
- field_types: []Index,
- field_names: []NullTerminatedString,
- field_inits: []Index,
- },
- .config = .{
- .@"trailing.captures_len.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
- .@"trailing.type_hash.?" = .@"payload.is_flags.is_reified",
- .@"trailing.field_types.len" = .@"payload.fields_len",
- .@"trailing.field_names.len" = .@"payload.fields_len",
- .@"trailing.field_inits.len" = .@"payload.fields_len",
- },
- },
.type_tuple = .{
.summary = .@"struct {...}",
.payload = TypeTuple,
@@ -5905,25 +5202,6 @@ pub const Tag = enum(u8) {
.@"trailing.field_values.len" = .@"payload.fields_len",
},
},
- .type_union = .{
- .summary = .@"{.payload.name%summary#\"}",
- .payload = TypeUnion,
- .trailing = struct {
- captures_len: ?u32,
- captures: ?[]CaptureValue,
- type_hash: ?u64,
- field_types: []Index,
- field_aligns: []Alignment,
- },
- .config = .{
- .@"trailing.captures_len.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?" = .@"payload.flags.any_captures",
- .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
- .@"trailing.type_hash.?" = .@"payload.is_flags.is_reified",
- .@"trailing.field_types.len" = .@"payload.fields_len",
- .@"trailing.field_aligns.len" = .@"payload.fields_len",
- },
- },
.type_function = .{
.summary = .@"fn (...) ... {.payload.return_type%summary}",
.payload = TypeFunction,
@@ -5941,6 +5219,93 @@ pub const Tag = enum(u8) {
},
},
+ .type_struct = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeStruct,
+ .trailing = struct {
+ type_hash: ?u64,
+ captures_len: ?u32,
+ captures: ?[]CaptureValue,
+ field_names: []NullTerminatedString,
+ field_types: []Index,
+ field_defaults: ?[]Index,
+ field_aligns: ?[]Alignment,
+ field_is_comptime_bits: ?[]u32,
+ field_runtime_order: ?[]u32,
+ field_offsets: []u32,
+ },
+ .config = .{
+ .@"trailing.type_hash.?" = .@"payload.flags.any_captures == .reified",
+ .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true",
+ .@"trailing.captures.?" = .@"payload.flags.any_captures == .true",
+ .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
+ .@"trailing.field_names.len" = .@"payload.fields_len",
+ .@"trailing.field_types.len" = .@"payload.fields_len",
+ .@"trailing.field_defaults.?" = .@"payload.flags.any_field_defaults",
+ .@"trailing.field_defaults.?.len" = .@"payload.fields_len",
+ .@"trailing.field_aligns.?" = .@"payload.flags.any_field_aligns",
+ .@"trailing.field_aligns.?.len" = .@"payload.fields_len",
+ .@"trailing.field_is_comptime_bits.?" = .@"payload.flags.any_comptime_fields",
+ .@"trailing.field_is_comptime_bits.?.len" = .@"(payload.fields_len + 31) / 32",
+ .@"trailing.field_runtime_order.?" = .@"payload.flags.layout == .auto",
+ .@"trailing.field_runtime_order.?.len" = .@"payload.fields_len",
+ .@"trailing.field_offsets.len" = .@"payload.fields_len",
+ },
+ },
+ .type_struct_packed_auto = struct_packed_encoding,
+ .type_struct_packed_explicit = struct_packed_encoding,
+ .type_struct_packed_auto_defaults = struct_packed_defaults_encoding,
+ .type_struct_packed_explicit_defaults = struct_packed_defaults_encoding,
+ .type_union = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeUnion,
+ .trailing = struct {
+ type_hash: ?u64,
+ captures_len: ?u32,
+ captures: ?[]CaptureValue,
+ field_types: []Index,
+ field_aligns: ?[]Alignment,
+ },
+ .config = .{
+ .@"trailing.type_hash.?" = .@"payload.flags.any_captures == .reified",
+ .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true",
+ .@"trailing.captures.?" = .@"payload.flags.any_captures == .true",
+ .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
+ .@"trailing.field_types.len" = .@"payload.fields_len",
+ .@"trailing.field_aligns.?" = .@"payloads.flags.any_field_aligns",
+ .@"trailing.field_aligns.?.len" = .@"payload.fields_len",
+ },
+ },
+ .type_union_packed_auto = union_packed_encoding,
+ .type_union_packed_explicit = union_packed_encoding,
+ .type_enum_auto = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeEnum,
+ .trailing = struct {
+ owner_union: ?Index,
+ zir_index: ?TrackedInst.Index,
+ type_hash: ?u64,
+ captures: ?[]CaptureValue,
+ field_names: []NullTerminatedString,
+ },
+ .config = .{
+ .@"trailing.owner_union.?" = .@"payload.captures_len == .generated_union_tag",
+ .@"trailing.zir_index.?" = .@"payload.captures_len != .generated_union_tag",
+ .@"trailing.type_hash.?" = .@"payload.captures_len == .reified",
+ .@"trailing.captures.?" = .@"payload.captures_len != .reified and payload.captures_len != .generated_enum_tag",
+ .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)",
+ .@"trailing.field_names.len" = .@"payload.fields_len",
+ },
+ },
+ .type_enum_explicit = enum_explicit_encoding,
+ .type_enum_nonexhaustive = enum_explicit_encoding,
+ .type_opaque = .{
+ .summary = .@"{.payload.name%summary#\"}",
+ .payload = TypeOpaque,
+ .trailing = struct { captures: []CaptureValue },
+ .config = .{ .@"trailing.captures.len" = .@"payload.captures_len" },
+ },
+
.undef = .{ .summary = .@"@as({.data%summary}, undefined)", .data = Index },
.simple_value = .{ .summary = .@"{.index%value#.}", .index = SimpleValue },
.ptr_nav = .{
@@ -5999,8 +5364,6 @@ pub const Tag = enum(u8) {
.int_small = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.value%value})", .payload = IntSmall },
.int_positive = .{},
.int_negative = .{},
- .int_lazy_align = .{ .summary = .@"@as({.payload.ty%summary}, @alignOf({.payload.lazy_ty%summary}))", .payload = IntLazy },
- .int_lazy_size = .{ .summary = .@"@as({.payload.ty%summary}, @sizeOf({.payload.lazy_ty%summary}))", .payload = IntLazy },
.error_set_error = .{ .summary = .@"@as({.payload.ty%summary}, error.@{.payload.name%summary})", .payload = Error },
.error_union_error = .{ .summary = .@"@as({.payload.ty%summary}, error.@{.payload.name%summary})", .payload = Error },
.error_union_payload = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.val%summary})", .payload = TypeValue },
@@ -6166,77 +5529,10 @@ pub const Tag = enum(u8) {
pub const Flags = packed struct(u32) {
cc: PackedCallingConvention,
is_var_args: bool,
- is_generic: bool,
has_comptime_bits: bool,
has_noalias_bits: bool,
is_noinline: bool,
- _: u9 = 0,
- };
- };
-
- /// Trailing:
- /// 0. captures_len: u32 // if `any_captures`
- /// 1. capture: CaptureValue // for each `captures_len`
- /// 2. type_hash: PackedU64 // if `is_reified`
- /// 3. field type: Index for each field; declaration order
- /// 4. field align: Alignment for each field; declaration order
- pub const TypeUnion = struct {
- name: NullTerminatedString,
- name_nav: Nav.Index.Optional,
- flags: Flags,
- /// This could be provided through the tag type, but it is more convenient
- /// to store it directly. This is also necessary for `dumpStatsFallible` to
- /// work on unresolved types.
- fields_len: u32,
- /// Only valid after .have_layout
- size: u32,
- /// Only valid after .have_layout
- padding: u32,
- namespace: NamespaceIndex,
- /// The enum that provides the list of field names and values.
- tag_ty: Index,
- zir_index: TrackedInst.Index,
-
- pub const Flags = packed struct(u32) {
- any_captures: bool,
- runtime_tag: LoadedUnionType.RuntimeTag,
- /// If false, the field alignment trailing data is omitted.
- any_aligned_fields: bool,
- layout: std.builtin.Type.ContainerLayout,
- status: LoadedUnionType.Status,
- requires_comptime: RequiresComptime,
- assumed_runtime_bits: bool,
- assumed_pointer_aligned: bool,
- alignment: Alignment,
- is_reified: bool,
- _: u12 = 0,
- };
- };
-
- /// Trailing:
- /// 0. captures_len: u32 // if `any_captures`
- /// 1. capture: CaptureValue // for each `captures_len`
- /// 2. type_hash: PackedU64 // if `is_reified`
- /// 3. type: Index for each fields_len
- /// 4. name: NullTerminatedString for each fields_len
- /// 5. init: Index for each fields_len // if tag is type_struct_packed_inits
- pub const TypeStructPacked = struct {
- name: NullTerminatedString,
- name_nav: Nav.Index.Optional,
- zir_index: TrackedInst.Index,
- fields_len: u32,
- namespace: NamespaceIndex,
- backing_int_ty: Index,
- names_map: MapIndex,
- flags: Flags,
-
- pub const Flags = packed struct(u32) {
- any_captures: bool = false,
- /// Dependency loop detection when resolving field inits.
- field_inits_wip: bool = false,
- inits_resolved: bool = false,
- is_reified: bool = false,
- _: u28 = 0,
+ _: u10 = 0,
};
};
@@ -6255,75 +5551,220 @@ pub const Tag = enum(u8) {
/// than coming up with some other scheme for the data.
///
/// Trailing:
- /// 0. captures_len: u32 // if `any_captures`
- /// 1. capture: CaptureValue // for each `captures_len`
- /// 2. type_hash: PackedU64 // if `is_reified`
- /// 3. type: Index for each field in declared order
- /// 4. if any_default_inits:
- /// init: Index // for each field in declared order
- /// 5. if any_aligned_fields:
- /// align: Alignment // for each field in declared order
- /// 6. if any_comptime_fields:
- /// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 0
- /// 7. if not is_extern:
- /// field_index: RuntimeOrder // for each field in runtime order
- /// 8. field_offset: u32 // for each field in declared order, undef until layout_resolved
+ /// 0. type_hash: PackedU64 // if `any_captures == .reified`
+ /// 1. captures_len: u32 // if `any_captures == .true`
+ /// 2. capture: CaptureValue // for each `captures_len`
+ /// 3. field_name: NullTerminatedString // for each `fields_len`
+ /// 4. field_type: Index // for each `fields_len`
+ /// 5. field_default: Index // if `any_field_defaults`; for each `fields_len`
+ /// 6. field_align: Alignment // if `any_field_aligns`; for each `fields_len`
+ /// 7. field_is_comptime_bits: u32 // if `any_comptime_fields`; minimum `u32` for `fields_len`; LSB is field 0
+ /// 8. field_runtime_order: RuntimeOrder // if `layout == .auto`; for each `fields_len`
+ /// 9. field_offset: u32 // for each `fields_len`
pub const TypeStruct = struct {
+ zir_index: TrackedInst.Index,
+
name: NullTerminatedString,
name_nav: Nav.Index.Optional,
- zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
+
fields_len: u32,
+ field_name_map: MapIndex,
+
+ /// Size in bytes of the whole struct. Always 0 until layout resolved.
+ size: u32,
+
flags: Flags,
+
+ pub const Flags = packed struct(u32) {
+ any_captures: enum(u2) { true, false, reified },
+
+ /// `packed` layout is represented separately by `TypeStructPacked`.
+ layout: enum(u1) { auto, @"extern" },
+
+ any_comptime_fields: bool,
+ any_field_defaults: bool,
+ any_field_aligns: bool,
+
+ /// Whether the struct is an OPV type. Always `false` until layout resolved.
+ /// The actual OPV is not cached, but caching this bit of state means we avoid
+ /// repeatedly doing redundant checks to find that the struct is not OPV!
+ has_one_possible_value: bool,
+ /// Like `has_one_possible_value`, but for a "noreturn" union (where all fields are noreturn).
+ has_no_possible_value: bool,
+ /// Whether the struct is comptime-only. Always `false` until layout resolved.
+ comptime_only: bool,
+ /// Alignment of the whole struct. Always `.none` until layout resolved.
+ alignment: Alignment,
+
+ _: u17 = 0,
+ };
+ };
+
+ /// Trailing:
+ /// 0. type_hash: PackedU64 // if `captures_len == .reified`
+ /// 1. capture: CaptureValue // if `captures_len != .reified`; for each `captures_len`
+ /// 2. field_name: NullTerminatedString // for each `fields_len`
+ /// 3. field_type: Index // for each `fields_len`
+ /// 4. field_default: Index // if item tag implies field defaults; for each `fields_len`
+ pub const TypeStructPacked = struct {
+ zir_index: TrackedInst.Index,
+ captures_len: enum(u32) {
+ reified = std.math.maxInt(u32),
+ _,
+ },
+
+ name: NullTerminatedString,
+ name_nav: Nav.Index.Optional,
+ namespace: NamespaceIndex,
+
+ /// The corresponding `PackedBackingMode` depends on the item's `Tag`.
+ backing_int_type: Index,
+
+ fields_len: u32,
+ field_name_map: MapIndex,
+ };
+
+ /// Field names are intentionally omitted---they are available in `enum_tag_type`.
+ ///
+ /// Trailing:
+ /// 0. type_hash: PackedU64 // if `any_captures == .reified`
+ /// 1. captures_len: u32 // if `any_captures == .true`
+ /// 2. capture: CaptureValue // if `any_captures == .true`; for each `captures_len`
+ /// 3. field_type: Index // for each `fields_len`
+ /// 4. field_align: Alignment // for each `fields_len` if `any_field_aligns`
+ pub const TypeUnion = struct {
+ zir_index: TrackedInst.Index,
+
+ name: NullTerminatedString,
+ name_nav: Nav.Index.Optional,
+ namespace: NamespaceIndex,
+ /// The enum that provides the list of field names and values.
+ enum_tag_type: Index,
+
+ /// This could be provided through the tag type, but it is more convenient
+ /// to store it directly. This is also necessary for `dumpStatsFallible` to
+ /// work on unresolved types.
+ /// MLUGG TODO: reconsider, because we resolve the tag type eagerly now.
+ fields_len: u32,
+
+ /// Always 0 until layout resolved.
size: u32,
+ /// Always 0 until layout resolved.
+ padding: u32,
+
+ flags: Flags,
pub const Flags = packed struct(u32) {
- any_captures: bool = false,
- is_extern: bool = false,
- known_non_opv: bool = false,
- requires_comptime: RequiresComptime = @enumFromInt(0),
- assumed_runtime_bits: bool = false,
- assumed_pointer_aligned: bool = false,
- any_comptime_fields: bool = false,
- any_default_inits: bool = false,
- any_aligned_fields: bool = false,
- /// `.none` until layout_resolved
- alignment: Alignment = @enumFromInt(0),
- /// Dependency loop detection when resolving struct alignment.
- alignment_wip: bool = false,
- /// Dependency loop detection when resolving field types.
- field_types_wip: bool = false,
- /// Dependency loop detection when resolving struct layout.
- layout_wip: bool = false,
- /// Indicates whether `size`, `alignment`, runtime field order, and
- /// field offets are populated.
- layout_resolved: bool = false,
- /// Dependency loop detection when resolving field inits.
- field_inits_wip: bool = false,
- /// Indicates whether `field_inits` has been resolved.
- inits_resolved: bool = false,
- // The types and all its fields have had their layout resolved. Even through pointer = false,
- // which `layout_resolved` does not ensure.
- fully_resolved: bool = false,
- is_reified: bool = false,
- _: u8 = 0,
+ any_captures: enum(u2) { true, false, reified },
+
+ /// Whether `enum_tag_type` was explicitly specified with `union(E)` syntax.
+ ///
+ /// For `union(enum(E))` syntax, this is `false`, but the generated enum tag type is
+ /// considered to have an explicitly specified integer tag type.
+ explicit_tag_type: bool,
+
+ /// `packed` layout is represented separately by `TypeStructPacked`.
+ layout: enum(u1) { auto, @"extern" },
+
+ any_field_aligns: bool,
+ runtime_tag: LoadedUnionType.RuntimeTag,
+
+ /// Whether the union is an OPV type. Always `false` until layout resolved.
+ /// The actual OPV is not cached, but caching this bit of state means we avoid
+ /// repeatedly doing redundant checks to find that the union is not OPV!
+ has_one_possible_value: bool,
+ /// Like `has_one_possible_value`, but for a "noreturn" union (where all fields are noreturn).
+ has_no_possible_value: bool,
+ /// Whether the union is comptime-only. Always `false` until layout resolved.
+ comptime_only: bool,
+ /// Alignment of the whole union. Always `.none` until layout resolved.
+ alignment: Alignment,
+
+ _: u16 = 0,
};
};
+ /// Field names are intentionally omitted---they are available in `enum_tag_type`.
+ ///
+ /// Trailing:
+ /// 0. type_hash: PackedU64 // if `captures_len == .reified`
+ /// 1. capture: CaptureValue // if `captures_len != .reified`; for each `captures_len`
+ /// 2. field_type: Index // for each `fields_len`
+ pub const TypeUnionPacked = struct {
+ zir_index: TrackedInst.Index,
+ captures_len: enum(u32) {
+ reified = std.math.maxInt(u32),
+ _,
+ },
+
+ name: NullTerminatedString,
+ name_nav: Nav.Index.Optional,
+ namespace: NamespaceIndex,
+
+ /// The corresponding `PackedBackingMode` depends on the item's `Tag`.
+ backing_int_type: Index,
+ /// Although packed unions do not semantically have a tag type, the compiler still assigns
+ /// them a "hypothetical" tag type.
+ enum_tag_type: Index,
+
+ /// This could be provided through the tag type, but it is more convenient
+ /// to store it directly. This is also necessary for `dumpStatsFallible` to
+ /// work on unresolved types.
+ /// MLUGG TODO: reconsider, because we resolve the tag type eagerly now.
+ fields_len: u32,
+ };
+
+ /// Trailing:
+ /// 0. owner_union: Index // if `captures_len == .generated_union_tag`
+ /// 1. zir_index: TrackedInst.Index // if `captures_len != .generated_union_tag`
+ /// 2. type_hash: PackedU64 // if `captures_len == .reified`
+ /// 3. capture: CaptureValue // if `captures_len` is not a named tag; for each `captures_len`
+ /// 4. field_value_map: MapIndex // if tag is not `.type_enum_auto`
+ /// 5. field_name: NullTerminatedString // for each `fields_len`
+ /// 6. field_value: Index // if tag is not `.type_enum_auto`; for each `fields_len`
+ pub const TypeEnum = struct {
+ captures_len: enum(u32) {
+ reified = std.math.maxInt(u32),
+ generated_union_tag = std.math.maxInt(u32) - 1,
+ _,
+ },
+
+ name: NullTerminatedString,
+ name_nav: Nav.Index.Optional,
+ namespace: NamespaceIndex,
+
+ /// An integer type which is used for the numerical value of the enum. Whether this was
+ /// user-provided or inferred by the compiler depends on the tag. Either way, the field
+ /// is populated immediately (i.e. does not require any type resolution).
+ int_tag_type: Index,
+
+ fields_len: u32,
+ field_name_map: MapIndex,
+ };
+
/// Trailing:
/// 0. capture: CaptureValue // for each `captures_len`
pub const TypeOpaque = struct {
- name: NullTerminatedString,
- name_nav: Nav.Index.Optional,
- /// Contains the declarations inside this opaque.
- namespace: NamespaceIndex,
- /// The index of the `opaque_decl` instruction.
zir_index: TrackedInst.Index,
- /// `std.math.maxInt(u32)` indicates this type is reified.
captures_len: u32,
+
+ name: NullTerminatedString,
+ name_nav: Nav.Index.Optional,
+ namespace: NamespaceIndex,
};
};
+/// Differentiates between user-provided and compiler-generated backing types for packed aggregates.
+pub const PackedBackingMode = enum(u1) {
+ /// The backing type was explicitly provided by the user, i.e. `packed struct(T)` or `packed union(T)`.
+ /// Type resolution simply *validates* that type.
+ explicit,
+ /// No backing type was explicitly provided by the user. Type layout resolution will populate the
+ /// backing type based on the field types; before then it is invalid (probably `.none`).
+ auto,
+};
+
/// State that is mutable during semantic analysis. This data is not used for
/// equality or hashing, except for `inferred_error_set` which is considered
/// to be part of the type of the function.
@@ -6536,10 +5977,8 @@ pub const Alignment = enum(u6) {
pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 };
pub fn get(slice: Slice, ip: *const InternPool) []Alignment {
- // TODO: implement @ptrCast between slices changing the length
const extra = ip.getLocalShared(slice.tid).extra.acquire();
- //const bytes: []u8 = @ptrCast(extra.view().items(.@"0")[slice.start..]);
- const bytes: []u8 = std.mem.sliceAsBytes(extra.view().items(.@"0")[slice.start..]);
+ const bytes: []u8 = @ptrCast(extra.view().items(.@"0")[slice.start..]);
return @ptrCast(bytes[0..slice.len]);
}
};
@@ -6596,55 +6035,6 @@ pub const Array = struct {
}
};
-/// Trailing:
-/// 0. owner_union: Index // if `zir_index == .none`
-/// 1. capture: CaptureValue // for each `captures_len`
-/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
-/// 3. field name: NullTerminatedString for each fields_len; declaration order
-/// 4. tag value: Index for each fields_len; declaration order
-pub const EnumExplicit = struct {
- name: NullTerminatedString,
- name_nav: Nav.Index.Optional,
- /// `std.math.maxInt(u32)` indicates this type is reified.
- captures_len: u32,
- namespace: NamespaceIndex,
- /// An integer type which is used for the numerical value of the enum, which
- /// has been explicitly provided by the enum declaration.
- int_tag_type: Index,
- fields_len: u32,
- /// Maps field names to declaration index.
- names_map: MapIndex,
- /// Maps field values to declaration index.
- /// If this is `none`, it means the trailing tag values are absent because
- /// they are auto-numbered.
- values_map: OptionalMapIndex,
- /// `none` means this is a generated tag type.
- /// There will be a trailing union type for which this is a tag.
- zir_index: TrackedInst.Index.Optional,
-};
-
-/// Trailing:
-/// 0. owner_union: Index // if `zir_index == .none`
-/// 1. capture: CaptureValue // for each `captures_len`
-/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
-/// 3. field name: NullTerminatedString for each fields_len; declaration order
-pub const EnumAuto = struct {
- name: NullTerminatedString,
- name_nav: Nav.Index.Optional,
- /// `std.math.maxInt(u32)` indicates this type is reified.
- captures_len: u32,
- namespace: NamespaceIndex,
- /// An integer type which is used for the numerical value of the enum, which
- /// was inferred by Zig based on the number of tags.
- int_tag_type: Index,
- fields_len: u32,
- /// Maps field names to declaration index.
- names_map: MapIndex,
- /// `none` means this is a generated tag type.
- /// There will be a trailing union type for which this is a tag.
- zir_index: TrackedInst.Index.Optional,
-};
-
pub const PackedU64 = packed struct(u64) {
a: u32,
b: u32,
@@ -6827,11 +6217,6 @@ pub const IntSmall = struct {
value: u32,
};
-pub const IntLazy = struct {
- ty: Index,
- lazy_ty: Index,
-};
-
/// A f64 value, broken up into 2 u32 parts.
pub const Float64 = struct {
piece0: u32,
@@ -6994,7 +6379,9 @@ pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void {
ip.src_hash_deps.deinit(gpa);
ip.nav_val_deps.deinit(gpa);
ip.nav_ty_deps.deinit(gpa);
- ip.interned_deps.deinit(gpa);
+ ip.func_ies_deps.deinit(gpa);
+ ip.type_layout_deps.deinit(gpa);
+ ip.type_inits_deps.deinit(gpa);
ip.zon_file_deps.deinit(gpa);
ip.embed_file_deps.deinit(gpa);
ip.namespace_deps.deinit(gpa);
@@ -7130,132 +6517,138 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.type_inferred_error_set => .{
.inferred_error_set_type = @enumFromInt(data),
},
-
- .type_opaque => .{ .opaque_type = ns: {
- const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data);
- if (extra.data.captures_len == std.math.maxInt(u32)) {
- break :ns .{ .reified = .{
- .zir_index = extra.data.zir_index,
- .type_hash = 0,
- } };
- }
- break :ns .{ .declared = .{
- .zir_index = extra.data.zir_index,
- .captures = .{ .owned = .{
- .tid = unwrapped_index.tid,
- .start = extra.end,
- .len = extra.data.captures_len,
- } },
- } };
- } },
-
- .type_struct => .{ .struct_type = ns: {
- const extra_list = unwrapped_index.getExtra(ip);
- const extra_items = extra_list.view().items(.@"0");
- const zir_index: TrackedInst.Index = @enumFromInt(extra_items[data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]);
- const flags: Tag.TypeStruct.Flags = @bitCast(@atomicLoad(u32, &extra_items[data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?], .unordered));
- const end_extra_index = data + @as(u32, @typeInfo(Tag.TypeStruct).@"struct".fields.len);
- if (flags.is_reified) {
- assert(!flags.any_captures);
- break :ns .{ .reified = .{
- .zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, end_extra_index).get(),
- } };
- }
- break :ns .{ .declared = .{
- .zir_index = zir_index,
- .captures = .{ .owned = if (flags.any_captures) .{
- .tid = unwrapped_index.tid,
- .start = end_extra_index + 1,
- .len = extra_list.view().items(.@"0")[end_extra_index],
- } else CaptureValue.Slice.empty },
- } };
- } },
-
- .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = ns: {
- const extra_list = unwrapped_index.getExtra(ip);
- const extra_items = extra_list.view().items(.@"0");
- const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]);
- const flags: Tag.TypeStructPacked.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?], .unordered));
- const end_extra_index = data + @as(u32, @typeInfo(Tag.TypeStructPacked).@"struct".fields.len);
- if (flags.is_reified) {
- assert(!flags.any_captures);
- break :ns .{ .reified = .{
- .zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, end_extra_index).get(),
- } };
- }
- break :ns .{ .declared = .{
- .zir_index = zir_index,
- .captures = .{ .owned = if (flags.any_captures) .{
- .tid = unwrapped_index.tid,
- .start = end_extra_index + 1,
- .len = extra_items[end_extra_index],
- } else CaptureValue.Slice.empty },
- } };
- } },
+ .type_function => .{ .func_type = extraFuncType(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
.type_tuple => .{ .tuple_type = extraTypeTuple(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
+
+ .type_struct => .{ .struct_type = ns: {
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra = extraDataTrail(extra_list, Tag.TypeStruct, data);
+ break :ns switch (extra.data.flags.any_captures) {
+ .reified => .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
+ } },
+ .false => .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .arg_ty = .none,
+ .captures = .{ .owned = .empty },
+ } },
+ .true => .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .arg_ty = .none,
+ .captures = .{ .owned = .{
+ .tid = unwrapped_index.tid,
+ .start = extra.end + 1,
+ .len = extra_list.view().items(.@"0")[extra.end],
+ } },
+ } },
+ };
+ } },
+ .type_struct_packed_auto,
+ .type_struct_packed_explicit,
+ .type_struct_packed_auto_defaults,
+ .type_struct_packed_explicit_defaults,
+ => .{ .struct_type = ns: {
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
+ break :ns switch (extra.data.captures_len) {
+ .reified => .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
+ } },
+ _ => .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .arg_ty = switch (item.tag) {
+ .type_struct_packed_auto, .type_struct_packed_auto_defaults => .none,
+ .type_struct_packed_explicit, .type_struct_packed_explicit_defaults => extra.data.backing_int_type,
+ else => unreachable,
+ },
+ .captures = .{ .owned = .{
+ .tid = unwrapped_index.tid,
+ .start = extra.end,
+ .len = @intFromEnum(extra.data.captures_len),
+ } },
+ } },
+ };
+ } },
.type_union => .{ .union_type = ns: {
const extra_list = unwrapped_index.getExtra(ip);
const extra = extraDataTrail(extra_list, Tag.TypeUnion, data);
- if (extra.data.flags.is_reified) {
- assert(!extra.data.flags.any_captures);
- break :ns .{ .reified = .{
+ break :ns switch (extra.data.flags.any_captures) {
+ .reified => .{ .reified = .{
.zir_index = extra.data.zir_index,
.type_hash = extraData(extra_list, PackedU64, extra.end).get(),
- } };
- }
+ } },
+ .false => .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .arg_ty = if (extra.data.flags.explicit_tag_type) extra.data.enum_tag_type else .none,
+ .captures = .{ .owned = .empty },
+ } },
+ .true => .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .arg_ty = if (extra.data.flags.explicit_tag_type) extra.data.enum_tag_type else .none,
+ .captures = .{ .owned = .{
+ .tid = unwrapped_index.tid,
+ .start = extra.end + 1,
+ .len = extra_list.view().items(.@"0")[extra.end],
+ } },
+ } },
+ };
+ } },
+ .type_union_packed_auto, .type_union_packed_explicit => .{ .union_type = ns: {
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data);
+ break :ns switch (extra.data.captures_len) {
+ .reified => .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
+ } },
+ _ => .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .arg_ty = switch (item.tag) {
+ .type_union_packed_auto => .none,
+ .type_union_packed_explicit => extra.data.backing_int_type,
+ else => unreachable,
+ },
+ .captures = .{ .owned = .{
+ .tid = unwrapped_index.tid,
+ .start = extra.end,
+ .len = @intFromEnum(extra.data.captures_len),
+ } },
+ } },
+ };
+ } },
+ .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: {
+ const extra_list = unwrapped_index.getExtra(ip);
+ const extra = extraDataTrail(extra_list, Tag.TypeEnum, data);
+ break :ns switch (extra.data.captures_len) {
+ .reified => .{ .reified = .{
+ .zir_index = @enumFromInt(extra_list.view().items(.@"0")[extra.end]),
+ .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(),
+ } },
+ .generated_union_tag => .{ .generated_union_tag = owner_union: {
+ break :owner_union @enumFromInt(extra_list.view().items(.@"0")[extra.end]);
+ } },
+ _ => .{ .declared = .{
+ .zir_index = @enumFromInt(extra_list.view().items(.@"0")[extra.end]),
+ .arg_ty = switch (item.tag) {
+ .type_enum_auto => .none,
+ .type_enum_explicit, .type_enum_nonexhaustive => extra.data.int_tag_type,
+ else => unreachable,
+ },
+ .captures = .{ .owned = .{
+ .tid = unwrapped_index.tid,
+ .start = extra.end + 1,
+ .len = @intFromEnum(extra.data.captures_len),
+ } },
+ } },
+ };
+ } },
+ .type_opaque => .{ .opaque_type = ns: {
+ const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data);
break :ns .{ .declared = .{
.zir_index = extra.data.zir_index,
- .captures = .{ .owned = if (extra.data.flags.any_captures) .{
- .tid = unwrapped_index.tid,
- .start = extra.end + 1,
- .len = extra_list.view().items(.@"0")[extra.end],
- } else CaptureValue.Slice.empty },
- } };
- } },
-
- .type_enum_auto => .{ .enum_type = ns: {
- const extra_list = unwrapped_index.getExtra(ip);
- const extra = extraDataTrail(extra_list, EnumAuto, data);
- const zir_index = extra.data.zir_index.unwrap() orelse {
- assert(extra.data.captures_len == 0);
- break :ns .{ .generated_tag = .{
- .union_type = @enumFromInt(extra_list.view().items(.@"0")[extra.end]),
- } };
- };
- if (extra.data.captures_len == std.math.maxInt(u32)) {
- break :ns .{ .reified = .{
- .zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
- } };
- }
- break :ns .{ .declared = .{
- .zir_index = zir_index,
- .captures = .{ .owned = .{
- .tid = unwrapped_index.tid,
- .start = extra.end,
- .len = extra.data.captures_len,
- } },
- } };
- } },
- .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: {
- const extra_list = unwrapped_index.getExtra(ip);
- const extra = extraDataTrail(extra_list, EnumExplicit, data);
- const zir_index = extra.data.zir_index.unwrap() orelse {
- assert(extra.data.captures_len == 0);
- break :ns .{ .generated_tag = .{
- .union_type = @enumFromInt(extra_list.view().items(.@"0")[extra.end]),
- } };
- };
- if (extra.data.captures_len == std.math.maxInt(u32)) {
- break :ns .{ .reified = .{
- .zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
- } };
- }
- break :ns .{ .declared = .{
- .zir_index = zir_index,
+ .arg_ty = .none,
.captures = .{ .owned = .{
.tid = unwrapped_index.tid,
.start = extra.end,
@@ -7263,7 +6656,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
} },
} };
} },
- .type_function => .{ .func_type = extraFuncType(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
.undef => .{ .undef = @enumFromInt(data) },
.opt_null => .{ .opt = .{
@@ -7390,17 +6782,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.storage = .{ .u64 = info.value },
} };
},
- .int_lazy_align, .int_lazy_size => |tag| {
- const info = extraData(unwrapped_index.getExtra(ip), IntLazy, data);
- return .{ .int = .{
- .ty = info.ty,
- .storage = switch (tag) {
- .int_lazy_align => .{ .lazy_align = info.lazy_ty },
- .int_lazy_size => .{ .lazy_size = info.lazy_ty },
- else => unreachable,
- },
- } };
- },
.float_f16 => .{ .float = .{
.ty = .f16_type,
.storage = .{ .f16 = @bitCast(@as(u16, @intCast(data))) },
@@ -7488,7 +6869,9 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
},
.type_array_small,
.type_vector,
- .type_struct_packed,
+ // MLUGG TODO: is this still possible? also, i hate .only_possible_value, it should die in a fire.
+ .type_struct_packed_auto,
+ .type_struct_packed_explicit,
=> .{ .aggregate = .{
.ty = ty,
.storage = .{ .elems = &.{} },
@@ -7496,11 +6879,15 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
// There is only one possible value precisely due to the
// fact that this values slice is fully populated!
- .type_struct, .type_struct_packed_inits => {
+ .type_struct,
+ // MLUGG TODO: is this still possible? also, i hate .only_possible_value, it should die in a fire.
+ .type_struct_packed_auto_defaults,
+ .type_struct_packed_explicit_defaults,
+ => {
const info = loadStructType(ip, ty);
return .{ .aggregate = .{
.ty = ty,
- .storage = .{ .elems = @ptrCast(info.field_inits.get(ip)) },
+ .storage = .{ .elems = @ptrCast(info.field_defaults.get(ip)) },
} };
},
@@ -7634,7 +7021,6 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke
.cc = type_function.data.flags.cc.unpack(),
.is_var_args = type_function.data.flags.is_var_args,
.is_noinline = type_function.data.flags.is_noinline,
- .is_generic = type_function.data.flags.is_generic,
};
}
@@ -7893,45 +7279,6 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
.map_index = map_index,
} };
}
-/// Like `getOrPutKey`, but asserts that the key already exists, and prepares to replace
-/// its shard entry with a new `Index` anyway. After finalizing this, the old index remains
-/// valid (in that `indexToKey` and similar queries will behave as before), but it will
-/// never be returned from a lookup (`getOrPutKey` etc).
-/// This is used by incremental compilation when an existing container type is outdated. In
-/// this case, the type must be recreated at a new `InternPool.Index`, but the old index must
-/// remain valid since now-unreferenced `AnalUnit`s may retain references to it. The old index
-/// will be cleaned up when the `Zcu` undergoes garbage collection.
-fn putKeyReplace(
- ip: *InternPool,
- io: Io,
- tid: Zcu.PerThread.Id,
- key: Key,
-) GetOrPutKey {
- const full_hash = key.hash64(ip);
- const hash: u32 = @truncate(full_hash >> 32);
- const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
- shard.mutate.map.mutex.lock(io, tid);
- errdefer shard.mutate.map.mutex.unlock(io);
- const map = shard.shared.map;
- const map_mask = map.header().mask();
- var map_index = hash;
- while (true) : (map_index += 1) {
- map_index &= map_mask;
- const entry = &map.entries[map_index];
- const index = entry.value;
- assert(index != .none); // key not present
- if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) {
- break; // we found the entry to replace
- }
- }
- return .{ .new = .{
- .ip = ip,
- .tid = tid,
- .io = io,
- .shard = shard,
- .map_index = map_index,
- } };
-}
pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index {
var gop = try ip.getOrPutKey(gpa, io, tid, key);
@@ -8249,23 +7596,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
.int => |int| b: {
assert(ip.isIntegerType(int.ty));
- switch (int.storage) {
- .u64, .i64, .big_int => {},
- .lazy_align, .lazy_size => |lazy_ty| {
- items.appendAssumeCapacity(.{
- .tag = switch (int.storage) {
- else => unreachable,
- .lazy_align => .int_lazy_align,
- .lazy_size => .int_lazy_size,
- },
- .data = try addExtra(extra, IntLazy{
- .ty = int.ty,
- .lazy_ty = lazy_ty,
- }),
- });
- return gop.put();
- },
- }
switch (int.ty) {
.u8_type => switch (int.storage) {
.big_int => |big_int| {
@@ -8282,7 +7612,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
});
break :b;
},
- .lazy_align, .lazy_size => unreachable,
},
.u16_type => switch (int.storage) {
.big_int => |big_int| {
@@ -8299,7 +7628,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
});
break :b;
},
- .lazy_align, .lazy_size => unreachable,
},
.u32_type => switch (int.storage) {
.big_int => |big_int| {
@@ -8316,7 +7644,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
});
break :b;
},
- .lazy_align, .lazy_size => unreachable,
},
.i32_type => switch (int.storage) {
.big_int => |big_int| {
@@ -8334,7 +7661,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
});
break :b;
},
- .lazy_align, .lazy_size => unreachable,
},
.usize_type => switch (int.storage) {
.big_int => |big_int| {
@@ -8355,7 +7681,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
break :b;
}
},
- .lazy_align, .lazy_size => unreachable,
},
.comptime_int_type => switch (int.storage) {
.big_int => |big_int| {
@@ -8390,7 +7715,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
break :b;
}
},
- .lazy_align, .lazy_size => unreachable,
},
else => {},
}
@@ -8427,7 +7751,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
const tag: Tag = if (big_int.positive) .int_positive else .int_negative;
try addInt(ip, gpa, io, tid, int.ty, tag, big_int.limbs);
},
- .lazy_align, .lazy_size => unreachable,
}
},
@@ -8468,7 +7791,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
assert(ip.isEnumType(enum_tag.ty));
switch (ip.indexToKey(enum_tag.ty)) {
.simple_type => assert(ip.isIntegerType(ip.typeOf(enum_tag.int))),
- .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).tag_ty),
+ .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).int_tag_type),
else => unreachable,
}
items.appendAssumeCapacity(.{
@@ -8735,304 +8058,57 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
return gop.put();
}
-pub fn getUnion(
- ip: *InternPool,
- gpa: Allocator,
- io: Io,
- tid: Zcu.PerThread.Id,
- un: Key.Union,
-) Allocator.Error!Index {
- var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
- defer gop.deinit();
- if (gop == .existing) return gop.existing;
- const local = ip.getLocal(tid);
- const items = local.getMutableItems(gpa, io);
- const extra = local.getMutableExtra(gpa, io);
- try items.ensureUnusedCapacity(1);
-
- assert(un.ty != .none);
- assert(un.val != .none);
- items.appendAssumeCapacity(.{
- .tag = .union_value,
- .data = try addExtra(extra, un),
- });
-
- return gop.put();
-}
-
-pub const UnionTypeInit = struct {
- flags: packed struct {
- runtime_tag: LoadedUnionType.RuntimeTag,
- any_aligned_fields: bool,
- layout: std.builtin.Type.ContainerLayout,
- status: LoadedUnionType.Status,
- requires_comptime: RequiresComptime,
- assumed_runtime_bits: bool,
- assumed_pointer_aligned: bool,
- alignment: Alignment,
- },
+pub fn getStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
fields_len: u32,
- enum_tag_ty: Index,
- /// May have length 0 which leaves the values unset until later.
- field_types: []const Index,
- /// May have length 0 which leaves the values unset until later.
- /// The logic for `any_aligned_fields` is asserted to have been done before
- /// calling this function.
- field_aligns: []const Alignment,
- key: union(enum) {
- declared: struct {
- zir_index: TrackedInst.Index,
- captures: []const CaptureValue,
- },
- declared_owned_captures: struct {
- zir_index: TrackedInst.Index,
- captures: CaptureValue.Slice,
- },
- reified: struct {
- zir_index: TrackedInst.Index,
- type_hash: u64,
- },
- },
-};
-
-pub fn getUnionType(
- ip: *InternPool,
- gpa: Allocator,
- io: Io,
- tid: Zcu.PerThread.Id,
- ini: UnionTypeInit,
- /// If it is known that there is an existing type with this key which is outdated,
- /// this is passed as `true`, and the type is replaced with one at a fresh index.
- replace_existing: bool,
-) Allocator.Error!WipNamespaceType.Result {
- const key: Key = .{ .union_type = switch (ini.key) {
- .declared => |d| .{ .declared = .{
- .zir_index = d.zir_index,
- .captures = .{ .external = d.captures },
- } },
- .declared_owned_captures => |d| .{ .declared = .{
- .zir_index = d.zir_index,
- .captures = .{ .owned = d.captures },
- } },
- .reified => |r| .{ .reified = .{
- .zir_index = r.zir_index,
- .type_hash = r.type_hash,
- } },
- } };
- var gop = if (replace_existing)
- ip.putKeyReplace(io, tid, key)
- else
- try ip.getOrPutKey(gpa, io, tid, key);
- defer gop.deinit();
- if (gop == .existing) return .{ .existing = gop.existing };
-
- const local = ip.getLocal(tid);
- const items = local.getMutableItems(gpa, io);
- try items.ensureUnusedCapacity(1);
- const extra = local.getMutableExtra(gpa, io);
-
- const align_elements_len = if (ini.flags.any_aligned_fields) (ini.fields_len + 3) / 4 else 0;
- const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4);
- try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".fields.len +
- // TODO: fmt bug
- // zig fmt: off
- switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
- .reified => 2, // type_hash: PackedU64
- } +
- // zig fmt: on
- ini.fields_len + // field types
- align_elements_len);
-
- const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{
- .flags = .{
- .any_captures = switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
- .reified => false,
- },
- .runtime_tag = ini.flags.runtime_tag,
- .any_aligned_fields = ini.flags.any_aligned_fields,
- .layout = ini.flags.layout,
- .status = ini.flags.status,
- .requires_comptime = ini.flags.requires_comptime,
- .assumed_runtime_bits = ini.flags.assumed_runtime_bits,
- .assumed_pointer_aligned = ini.flags.assumed_pointer_aligned,
- .alignment = ini.flags.alignment,
- .is_reified = switch (ini.key) {
- .declared, .declared_owned_captures => false,
- .reified => true,
- },
- },
- .fields_len = ini.fields_len,
- .size = std.math.maxInt(u32),
- .padding = std.math.maxInt(u32),
- .name = undefined, // set by `finish`
- .name_nav = undefined, // set by `finish`
- .namespace = undefined, // set by `finish`
- .tag_ty = ini.enum_tag_ty,
- .zir_index = switch (ini.key) {
- inline else => |x| x.zir_index,
- },
- });
-
- items.appendAssumeCapacity(.{
- .tag = .type_union,
- .data = extra_index,
- });
-
- switch (ini.key) {
- .declared => |d| if (d.captures.len != 0) {
- extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
- },
- .declared_owned_captures => |d| if (d.captures.len != 0) {
- extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
- },
- .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
- }
-
- // field types
- if (ini.field_types.len > 0) {
- assert(ini.field_types.len == ini.fields_len);
- extra.appendSliceAssumeCapacity(.{@ptrCast(ini.field_types)});
- } else {
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len);
- }
-
- // field alignments
- if (ini.flags.any_aligned_fields) {
- extra.appendNTimesAssumeCapacity(.{align_element}, align_elements_len);
- if (ini.field_aligns.len > 0) {
- assert(ini.field_aligns.len == ini.fields_len);
- @memcpy((Alignment.Slice{
- .tid = tid,
- .start = @intCast(extra.mutate.len - align_elements_len),
- .len = @intCast(ini.field_aligns.len),
- }).get(ip), ini.field_aligns);
- }
- } else {
- assert(ini.field_aligns.len == 0);
- }
-
- return .{ .wip = .{
- .tid = tid,
- .index = gop.put(),
- .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?,
- .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?,
- .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?,
- } };
-}
-
-pub const WipNamespaceType = struct {
- tid: Zcu.PerThread.Id,
- index: Index,
- type_name_extra_index: u32,
- namespace_extra_index: u32,
- name_nav_extra_index: u32,
-
- pub fn setName(
- wip: WipNamespaceType,
- ip: *InternPool,
- type_name: NullTerminatedString,
- /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise.
- /// This is also `.none` if we use `.parent` because we are the root struct type for a file.
- name_nav: Nav.Index.Optional,
- ) void {
- const extra = ip.getLocalShared(wip.tid).extra.acquire();
- const extra_items = extra.view().items(.@"0");
- extra_items[wip.type_name_extra_index] = @intFromEnum(type_name);
- extra_items[wip.name_nav_extra_index] = @intFromEnum(name_nav);
- }
-
- pub fn finish(
- wip: WipNamespaceType,
- ip: *InternPool,
- namespace: NamespaceIndex,
- ) Index {
- const extra = ip.getLocalShared(wip.tid).extra.acquire();
- const extra_items = extra.view().items(.@"0");
-
- extra_items[wip.namespace_extra_index] = @intFromEnum(namespace);
-
- return wip.index;
- }
-
- pub fn cancel(wip: WipNamespaceType, ip: *InternPool, tid: Zcu.PerThread.Id) void {
- ip.remove(tid, wip.index);
- }
-
- pub const Result = union(enum) {
- wip: WipNamespaceType,
- existing: Index,
- };
-};
-
-pub const StructTypeInit = struct {
layout: std.builtin.Type.ContainerLayout,
- fields_len: u32,
- known_non_opv: bool,
- requires_comptime: RequiresComptime,
+ /// The following only applies if `layout == .@"packed"`; this field is ignored otherwise.
+ ///
+ /// The explicitly specified backing integer type. `.none` means the backing integer is inferred
+ /// by the compiler. Asserts that this is an integer type.
+ explicit_packed_backing_type: Index,
any_comptime_fields: bool,
- any_default_inits: bool,
- inits_resolved: bool,
- any_aligned_fields: bool,
+ any_field_defaults: bool,
+ any_field_aligns: bool,
key: union(enum) {
declared: struct {
zir_index: TrackedInst.Index,
captures: []const CaptureValue,
},
- declared_owned_captures: struct {
- zir_index: TrackedInst.Index,
- captures: CaptureValue.Slice,
- },
reified: struct {
zir_index: TrackedInst.Index,
type_hash: u64,
},
},
-};
-
-pub fn getStructType(
- ip: *InternPool,
- gpa: Allocator,
- io: Io,
- tid: Zcu.PerThread.Id,
- ini: StructTypeInit,
- /// If it is known that there is an existing type with this key which is outdated,
- /// this is passed as `true`, and the type is replaced with one at a fresh index.
- replace_existing: bool,
-) Allocator.Error!WipNamespaceType.Result {
+}) Allocator.Error!WipContainerType.Result {
const key: Key = .{ .struct_type = switch (ini.key) {
.declared => |d| .{ .declared = .{
.zir_index = d.zir_index,
+ .arg_ty = switch (ini.layout) {
+ .auto, .@"extern" => .none,
+ .@"packed" => ini.explicit_packed_backing_type,
+ },
.captures = .{ .external = d.captures },
} },
- .declared_owned_captures => |d| .{ .declared = .{
- .zir_index = d.zir_index,
- .captures = .{ .owned = d.captures },
- } },
.reified => |r| .{ .reified = .{
.zir_index = r.zir_index,
.type_hash = r.type_hash,
} },
} };
- var gop = if (replace_existing)
- ip.putKeyReplace(io, tid, key)
- else
- try ip.getOrPutKey(gpa, io, tid, key);
+ var gop = try ip.getOrPutKey(gpa, io, tid, key);
defer gop.deinit();
if (gop == .existing) return .{ .existing = gop.existing };
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
+ try items.ensureUnusedCapacity(1);
- const names_map = try ip.addMap(gpa, io, tid, ini.fields_len);
+ const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
errdefer local.mutate.maps.len -= 1;
- const zir_index = switch (ini.key) {
- inline else => |x| x.zir_index,
+ const zir_index, const type_hash_captures_extra_len = switch (ini.key) {
+ .declared => |d| .{ d.zir_index, d.captures.len + @intFromBool(ini.layout != .@"packed") },
+ .reified => |r| .{ r.zir_index, 2 },
};
const is_extern = switch (ini.layout) {
@@ -9040,160 +8116,579 @@ pub fn getStructType(
.@"extern" => true,
.@"packed" => {
try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStructPacked).@"struct".fields.len +
- // TODO: fmt bug
- // zig fmt: off
- switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
- .reified => 2, // type_hash: PackedU64
- } +
- // zig fmt: on
- ini.fields_len + // types
- ini.fields_len + // names
- ini.fields_len); // inits
+ type_hash_captures_extra_len +
+ ini.fields_len + // field_name
+ ini.fields_len + // field_type
+ (if (ini.any_field_defaults) ini.fields_len else 0)); // field_default
+
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{
+ .zir_index = zir_index,
+ .captures_len = switch (ini.key) {
+ .declared => |d| @enumFromInt(d.captures.len),
+ .reified => .reified,
+ },
.name = undefined, // set by `finish`
.name_nav = undefined, // set by `finish`
- .zir_index = zir_index,
- .fields_len = ini.fields_len,
.namespace = undefined, // set by `finish`
- .backing_int_ty = .none,
- .names_map = names_map,
- .flags = .{
- .any_captures = switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
- .reified => false,
- },
- .field_inits_wip = false,
- .inits_resolved = ini.inits_resolved,
- .is_reified = switch (ini.key) {
- .declared, .declared_owned_captures => false,
- .reified => true,
- },
- },
- });
- try items.append(.{
- .tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed,
- .data = extra_index,
+ .backing_int_type = ini.explicit_packed_backing_type,
+ .fields_len = ini.fields_len,
+ .field_name_map = field_name_map,
});
switch (ini.key) {
- .declared => |d| if (d.captures.len != 0) {
- extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
- },
- .declared_owned_captures => |d| if (d.captures.len != 0) {
- extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
- },
- .reified => |r| {
- _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));
- },
+ .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
+ .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
}
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len);
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(OptionalNullTerminatedString.none)}, ini.fields_len);
- if (ini.any_default_inits) {
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len);
+ const field_names_start = extra.mutate.len;
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type
+ if (ini.any_field_defaults) {
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_default
}
+ items.appendAssumeCapacity(.{
+ .tag = switch (ini.explicit_packed_backing_type) {
+ .none => if (ini.any_field_defaults) .type_struct_packed_auto_defaults else .type_struct_packed_auto,
+ else => if (ini.any_field_defaults) .type_struct_packed_explicit_defaults else .type_struct_packed_explicit,
+ },
+ .data = extra_index,
+ });
return .{ .wip = .{
- .tid = tid,
.index = gop.put(),
- .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?,
- .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?,
- .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?,
+ .tid = tid,
+ .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?,
+ .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?,
+ .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?,
+ .tag_type_index = null,
+ .fields_len = ini.fields_len,
+ .field_name_map = field_name_map,
+ .field_names_start = field_names_start,
+ .field_comptime_bits_start = null,
} };
},
};
- const align_elements_len = if (ini.any_aligned_fields) (ini.fields_len + 3) / 4 else 0;
- const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4);
- const comptime_elements_len = if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0;
-
try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStruct).@"struct".fields.len +
- // TODO: fmt bug
- // zig fmt: off
- switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
- .reified => 2, // type_hash: PackedU64
- } +
- // zig fmt: on
- (ini.fields_len * 5) + // types, names, inits, runtime order, offsets
- align_elements_len + comptime_elements_len +
- 1); // names_map
+ type_hash_captures_extra_len +
+ ini.fields_len + // field_name
+ ini.fields_len + // field_type
+ (if (ini.any_field_defaults) ini.fields_len else 0) + // field_default
+ (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0) + // field_align
+ (if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0) + // field_is_comptime_bits
+ (if (!is_extern) ini.fields_len else 0) + // field_runtime_order
+ ini.fields_len); // field_offset
+
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{
+ .zir_index = zir_index,
.name = undefined, // set by `finish`
.name_nav = undefined, // set by `finish`
- .zir_index = zir_index,
.namespace = undefined, // set by `finish`
.fields_len = ini.fields_len,
- .size = std.math.maxInt(u32),
+ .field_name_map = field_name_map,
+ .size = 0,
.flags = .{
.any_captures = switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
- .reified => false,
+ .declared => |d| if (d.captures.len != 0) .true else .false,
+ .reified => .reified,
},
- .is_extern = is_extern,
- .known_non_opv = ini.known_non_opv,
- .requires_comptime = ini.requires_comptime,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
+ .layout = if (is_extern) .@"extern" else .auto,
.any_comptime_fields = ini.any_comptime_fields,
- .any_default_inits = ini.any_default_inits,
- .any_aligned_fields = ini.any_aligned_fields,
+ .any_field_defaults = ini.any_field_defaults,
+ .any_field_aligns = ini.any_field_aligns,
+ .has_one_possible_value = false,
+ .has_no_possible_value = false,
+ .comptime_only = false,
.alignment = .none,
- .alignment_wip = false,
- .field_types_wip = false,
- .layout_wip = false,
- .layout_resolved = false,
- .field_inits_wip = false,
- .inits_resolved = ini.inits_resolved,
- .fully_resolved = false,
- .is_reified = switch (ini.key) {
- .declared, .declared_owned_captures => false,
- .reified => true,
- },
},
});
- try items.append(.{
+ switch (ini.key) {
+ .declared => |d| if (d.captures.len != 0) {
+ extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
+ extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
+ },
+ .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
+ }
+ const field_names_start = extra.mutate.len;
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type
+ if (ini.any_field_defaults) {
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_default
+ }
+ if (ini.any_field_aligns) {
+ extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align
+ }
+ const field_comptime_bits_start: ?u32 = if (ini.any_comptime_fields) start: {
+ const start = extra.mutate.len;
+ extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 31) / 32); // field_is_comptime_bits
+ break :start start;
+ } else null;
+ if (!is_extern) {
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len); // field_runtime_order
+ }
+ extra.appendNTimesAssumeCapacity(.{0}, ini.fields_len); // field_offset
+ items.appendAssumeCapacity(.{
.tag = .type_struct,
.data = extra_index,
});
+ return .{ .wip = .{
+ .index = gop.put(),
+ .tid = tid,
+ .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?,
+ .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?,
+ .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?,
+ .tag_type_index = null,
+ .fields_len = ini.fields_len,
+ .field_name_map = field_name_map,
+ .field_names_start = field_names_start,
+ .field_comptime_bits_start = field_comptime_bits_start,
+ } };
+}
+
+pub fn getUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
+ fields_len: u32,
+ layout: std.builtin.Type.ContainerLayout,
+ /// The explicitly specified backing integer type for a `packed union`.
+ /// `.none` means the backing integer is inferred by the compiler. If set,
+ /// must be an integer type. If the union is not packed, must be `.none`.
+ explicit_packed_backing_type: Index,
+ runtime_tag: LoadedUnionType.RuntimeTag,
+ /// `true` for `union(T)`, but `false` for anything else, including `union(enum(T))`.
+ have_explicit_enum_tag: bool,
+ any_field_aligns: bool,
+ key: union(enum) {
+ declared: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+ /// This is the `T` in one of the following:
+ /// * `union(T)` (enum tag type)
+ /// * `union(enum(T))` (int tag type)
+ /// * `packed union(T)` (int backing type)
+ /// Or `.none` otherwise.
+ arg_ty: InternPool.Index,
+ },
+ reified: struct {
+ zir_index: TrackedInst.Index,
+ type_hash: u64,
+ },
+ },
+}) Allocator.Error!WipContainerType.Result {
+ if (ini.explicit_packed_backing_type != .none) {
+ assert(ip.zigTypeTag(ini.explicit_packed_backing_type) == .int);
+ if (ini.key == .declared) assert(ini.key.declared.arg_ty == ini.explicit_packed_backing_type);
+ }
+ const key: Key = .{ .union_type = switch (ini.key) {
+ .declared => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .arg_ty = d.arg_ty,
+ .captures = .{ .external = d.captures },
+ } },
+ .reified => |r| .{ .reified = .{
+ .zir_index = r.zir_index,
+ .type_hash = r.type_hash,
+ } },
+ } };
+ var gop = try ip.getOrPutKey(gpa, io, tid, key);
+ defer gop.deinit();
+ if (gop == .existing) return .{ .existing = gop.existing };
+
+ const local = ip.getLocal(tid);
+ const items = local.getMutableItems(gpa, io);
+ const extra = local.getMutableExtra(gpa, io);
+ try items.ensureUnusedCapacity(1);
+
+ const zir_index, const type_hash_captures_extra_len = switch (ini.key) {
+ .declared => |d| .{ d.zir_index, d.captures.len + @intFromBool(ini.layout != .@"packed") },
+ .reified => |r| .{ r.zir_index, 2 },
+ };
+
+ const is_extern = switch (ini.layout) {
+ .auto => false,
+ .@"extern" => true,
+ .@"packed" => {
+ try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnionPacked).@"struct".fields.len +
+ type_hash_captures_extra_len +
+ ini.fields_len); // field_type
+
+ const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{
+ .zir_index = zir_index,
+ .captures_len = switch (ini.key) {
+ .declared => |d| @enumFromInt(d.captures.len),
+ .reified => .reified,
+ },
+ .name = undefined, // set by `finish`
+ .name_nav = undefined, // set by `finish`
+ .namespace = undefined, // set by `finish`
+ .backing_int_type = ini.explicit_packed_backing_type,
+ .enum_tag_type = .none, // set by `setTagType`
+ .fields_len = ini.fields_len,
+ });
+ switch (ini.key) {
+ .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
+ .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
+ }
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type
+ items.appendAssumeCapacity(.{
+ .tag = switch (ini.explicit_packed_backing_type) {
+ .none => .type_union_packed_auto,
+ else => .type_union_packed_explicit,
+ },
+ .data = extra_index,
+ });
+ return .{
+ .wip = .{
+ .index = gop.put(),
+ .tid = tid,
+ .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?,
+ .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?,
+ .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?,
+ .tag_type_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "enum_tag_type").?,
+ .fields_len = 0, // the fields come from the enum, so nothing to set
+ .field_name_map = undefined,
+ .field_names_start = undefined,
+ .field_comptime_bits_start = undefined,
+ },
+ };
+ },
+ };
+
+ try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".fields.len +
+ type_hash_captures_extra_len +
+ ini.fields_len + // field_type
+ (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0)); // field_align
+
+ const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{
+ .zir_index = zir_index,
+ .name = undefined, // set by `finish`
+ .name_nav = undefined, // set by `finish`
+ .namespace = undefined, // set by `finish`
+ .enum_tag_type = .none, // set by `setTagType`
+ .fields_len = ini.fields_len,
+ .size = 0,
+ .padding = 0,
+ .flags = .{
+ .any_captures = switch (ini.key) {
+ .declared => |d| if (d.captures.len != 0) .true else .false,
+ .reified => .reified,
+ },
+ .explicit_tag_type = ini.have_explicit_enum_tag,
+ .layout = if (is_extern) .@"extern" else .auto,
+ .any_field_aligns = ini.any_field_aligns,
+ .runtime_tag = ini.runtime_tag,
+ .has_one_possible_value = false,
+ .has_no_possible_value = false,
+ .comptime_only = false,
+ .alignment = .none,
+ },
+ });
switch (ini.key) {
.declared => |d| if (d.captures.len != 0) {
extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
},
- .declared_owned_captures => |d| if (d.captures.len != 0) {
- extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
+ .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
+ }
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type
+ if (ini.any_field_aligns) {
+ extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align
+ }
+ items.appendAssumeCapacity(.{
+ .tag = .type_union,
+ .data = extra_index,
+ });
+ return .{
+ .wip = .{
+ .index = gop.put(),
+ .tid = tid,
+ .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?,
+ .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?,
+ .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?,
+ .tag_type_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "enum_tag_type").?,
+ .fields_len = 0, // the fields come from the enum, so nothing to set
+ .field_name_map = undefined,
+ .field_names_start = undefined,
+ .field_comptime_bits_start = undefined,
+ },
+ };
+}
+
+pub fn getEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
+ fields_len: u32,
+ /// For `enum(T)` or `union(enum(T))`, this is `T`. Asserts `T` is an integer type.
+ /// Otherwise, `.none`.
+ explicit_int_tag_type: Index,
+ nonexhaustive: bool,
+ key: union(enum) {
+ declared: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+ },
+ reified: struct {
+ zir_index: TrackedInst.Index,
+ type_hash: u64,
+ },
+ generated_union_tag: Index,
+ },
+}) Allocator.Error!WipContainerType.Result {
+ const key: Key = .{ .enum_type = switch (ini.key) {
+ .declared => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .arg_ty = ini.explicit_int_tag_type,
+ .captures = .{ .external = d.captures },
+ } },
+ .reified => |r| .{ .reified = .{
+ .zir_index = r.zir_index,
+ .type_hash = r.type_hash,
+ } },
+ .generated_union_tag => |u| .{ .generated_union_tag = u },
+ } };
+ var gop = try ip.getOrPutKey(gpa, io, tid, key);
+ defer gop.deinit();
+ if (gop == .existing) return .{ .existing = gop.existing };
+
+ const local = ip.getLocal(tid);
+ const items = local.getMutableItems(gpa, io);
+ const extra = local.getMutableExtra(gpa, io);
+ try items.ensureUnusedCapacity(1);
+
+ const tag: Tag, const have_values: bool = if (ini.nonexhaustive)
+ .{ .type_enum_nonexhaustive, true }
+ else if (ini.explicit_int_tag_type != .none)
+ .{ .type_enum_explicit, true }
+ else
+ .{ .type_enum_auto, false };
+
+ const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
+ errdefer local.mutate.maps.len -= 1;
+
+ const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined;
+ errdefer local.mutate.maps.len -= @intFromBool(have_values);
+
+ try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".fields.len +
+ switch (ini.key) {
+ .declared => |d| 1 + d.captures.len, // `zir_index` and `capture`
+ .reified => 3, // `zir_index` and `type_hash`
+ .generated_union_tag => 1, // owner_union
+ } +
+ @intFromBool(have_values) + // field_value_map
+ ini.fields_len + // field_name
+ (if (have_values) ini.fields_len else 0)); // field_value
+
+ const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{
+ .captures_len = switch (ini.key) {
+ .declared => |d| @enumFromInt(d.captures.len),
+ .reified => .reified,
+ .generated_union_tag => .generated_union_tag,
+ },
+ .name = undefined, // set by `finish`
+ .name_nav = undefined, // set by `finish`
+ .namespace = undefined, // set by `finish`
+ .int_tag_type = ini.explicit_int_tag_type,
+ .fields_len = ini.fields_len,
+ .field_name_map = field_name_map,
+ });
+ switch (ini.key) {
+ .declared => |d| {
+ extra.appendAssumeCapacity(.{@intFromEnum(d.zir_index)}); // zir_index
+ extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}); // capture
},
.reified => |r| {
- _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));
+ extra.appendAssumeCapacity(.{@intFromEnum(r.zir_index)}); // zir_index
+ _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)); // type_hash
+ },
+ .generated_union_tag => |owner_union| {
+ extra.appendAssumeCapacity(.{@intFromEnum(owner_union)}); // owner_union
},
}
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len);
- extra.appendAssumeCapacity(.{@intFromEnum(names_map)});
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(OptionalNullTerminatedString.none)}, ini.fields_len);
- if (ini.any_default_inits) {
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len);
- }
- if (ini.any_aligned_fields) {
- extra.appendNTimesAssumeCapacity(.{align_element}, align_elements_len);
- }
- if (ini.any_comptime_fields) {
- extra.appendNTimesAssumeCapacity(.{0}, comptime_elements_len);
- }
- if (ini.layout == .auto) {
- extra.appendNTimesAssumeCapacity(.{@intFromEnum(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len);
- }
- extra.appendNTimesAssumeCapacity(.{std.math.maxInt(u32)}, ini.fields_len);
+ if (have_values) extra.appendAssumeCapacity(.{@intFromEnum(field_value_map)});
+ const field_names_start = extra.mutate.len;
+ extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name
+ if (have_values) extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_value
+ items.appendAssumeCapacity(.{
+ .tag = tag,
+ .data = extra_index,
+ });
return .{ .wip = .{
+ .index = gop.put(),
.tid = tid,
+ .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?,
+ .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?,
+ .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?,
+ .tag_type_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "int_tag_type").?,
+ .fields_len = ini.fields_len,
+ .field_name_map = field_name_map,
+ .field_names_start = field_names_start,
+ .field_comptime_bits_start = null,
+ } };
+}
+
+pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+}) Allocator.Error!WipContainerType.Result {
+ var gop = try ip.getOrPutKey(gpa, io, tid, .{ .opaque_type = .{ .declared = .{
+ .zir_index = ini.zir_index,
+ .captures = .{ .external = ini.captures },
+ .arg_ty = .none,
+ } } });
+ defer gop.deinit();
+ if (gop == .existing) return .{ .existing = gop.existing };
+
+ const local = ip.getLocal(tid);
+ const items = local.getMutableItems(gpa, io);
+ const extra = local.getMutableExtra(gpa, io);
+ try items.ensureUnusedCapacity(1);
+
+ try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + ini.captures.len);
+ const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{
+ .zir_index = ini.zir_index,
+ .captures_len = @intCast(ini.captures.len),
+ .name = undefined, // set by `finish`
+ .name_nav = undefined, // set by `finish`
+ .namespace = undefined, // set by `finish`
+ });
+ extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)});
+ items.appendAssumeCapacity(.{
+ .tag = .type_opaque,
+ .data = extra_index,
+ });
+ return .{ .wip = .{
.index = gop.put(),
- .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?,
- .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?,
- .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?,
+ .tid = tid,
+ .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?,
+ .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?,
+ .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?,
+ .tag_type_index = null,
+ .fields_len = 0,
+ .field_name_map = undefined,
+ .field_names_start = undefined,
+ .field_comptime_bits_start = undefined,
} };
}
+pub const WipContainerType = struct {
+ index: Index,
+ tid: Zcu.PerThread.Id,
+ type_name_index: u32,
+ name_nav_index: u32,
+ namespace_index: u32,
+
+ tag_type_index: ?u32,
+
+ fields_len: u32,
+ field_name_map: MapIndex,
+ field_names_start: u32,
+ field_comptime_bits_start: ?u32,
+
+ pub fn setName(
+ wip: WipContainerType,
+ ip: *InternPool,
+ type_name: NullTerminatedString,
+ /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise.
+ /// This is also `.none` if we use `.parent` because we are the root struct type for a file.
+ name_nav: Nav.Index.Optional,
+ ) void {
+ const extra = ip.getLocalShared(wip.tid).extra.acquire();
+ const extra_items = extra.view().items(.@"0");
+ extra_items[wip.type_name_index] = @intFromEnum(type_name);
+ extra_items[wip.name_nav_index] = @intFromEnum(name_nav);
+ }
+
+ pub fn setTagType(
+ wip: WipContainerType,
+ ip: *InternPool,
+ tag_ty: Index,
+ ) void {
+ const extra = ip.getLocalShared(wip.tid).extra.acquire();
+ const extra_items = extra.view().items(.@"0");
+ const i = wip.tag_type_index.?;
+ const old_val: InternPool.Index = @enumFromInt(extra_items[i]);
+ assert(old_val == .none);
+ assert(tag_ty != .none);
+ extra_items[i] = @intFromEnum(tag_ty);
+ }
+
+ /// Returns the already-existing field with the same name, if any.
+ pub fn nextField(
+ wip: WipContainerType,
+ ip: *InternPool,
+ name: NullTerminatedString,
+ marked_comptime: bool,
+ ) ?u32 {
+ assert(wip.fields_len > 0);
+ const extra = ip.getLocalShared(wip.tid).extra.acquire();
+ const extra_items = extra.view().items(.@"0");
+ const map = wip.field_name_map.get(ip);
+ const field_idx = map.count();
+ assert(field_idx < wip.fields_len);
+ const names: []NullTerminatedString = @ptrCast(extra_items[wip.field_names_start..][0..wip.fields_len]);
+ const adapter: NullTerminatedString.Adapter = .{ .strings = names[0..field_idx] };
+ const gop = map.getOrPutAssumeCapacityAdapted(name, adapter);
+ if (gop.found_existing) return @intCast(gop.index);
+ names[field_idx] = name;
+ if (wip.field_comptime_bits_start) |start_idx| {
+ if (marked_comptime) {
+ extra_items[start_idx + field_idx / 32] |= @as(u32, 1) << @intCast(field_idx % 32);
+ }
+ } else {
+ assert(!marked_comptime);
+ }
+ return null;
+ }
+
+ pub fn finish(
+ wip: WipContainerType,
+ ip: *InternPool,
+ namespace: NamespaceIndex,
+ ) Index {
+ const extra = ip.getLocalShared(wip.tid).extra.acquire();
+ const extra_items = extra.view().items(.@"0");
+
+ extra_items[wip.namespace_index] = @intFromEnum(namespace);
+
+ if (wip.fields_len > 0) {
+ assert(wip.field_name_map.get(ip).count() == wip.fields_len);
+ }
+ if (wip.tag_type_index) |i| {
+ const tag_ty: Index = @enumFromInt(extra_items[i]);
+ assert(tag_ty != .none);
+ }
+
+ return wip.index;
+ }
+
+ pub fn cancel(wip: WipContainerType, ip: *InternPool, tid: Zcu.PerThread.Id) void {
+ ip.remove(tid, wip.index);
+ }
+
+ pub const Result = union(enum) {
+ wip: WipContainerType,
+ existing: Index,
+ };
+};
+
+pub fn getUnion(
+ ip: *InternPool,
+ gpa: Allocator,
+ io: Io,
+ tid: Zcu.PerThread.Id,
+ un: Key.Union,
+) Allocator.Error!Index {
+ var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
+ defer gop.deinit();
+ if (gop == .existing) return gop.existing;
+ const local = ip.getLocal(tid);
+ const items = local.getMutableItems(gpa, io);
+ const extra = local.getMutableExtra(gpa, io);
+ try items.ensureUnusedCapacity(1);
+
+ assert(un.ty != .none);
+ assert(un.val != .none);
+ items.appendAssumeCapacity(.{
+ .tag = .union_value,
+ .data = try addExtra(extra, un),
+ });
+
+ return gop.put();
+}
+
pub const TupleTypeInit = struct {
types: []const Index,
/// These elements may be `none`, indicating runtime-known.
@@ -9252,10 +8747,7 @@ pub const GetFuncTypeKey = struct {
/// `null` means generic.
cc: ?std.builtin.CallingConvention = .auto,
is_var_args: bool = false,
- is_generic: bool = false,
is_noinline: bool = false,
- section_is_generic: bool = false,
- addrspace_is_generic: bool = false,
};
pub fn getFuncType(
@@ -9293,7 +8785,6 @@ pub fn getFuncType(
.is_var_args = key.is_var_args,
.has_comptime_bits = key.comptime_bits != 0,
.has_noalias_bits = key.noalias_bits != 0,
- .is_generic = key.is_generic,
.is_noinline = key.is_noinline,
},
});
@@ -9480,7 +8971,6 @@ pub const GetFuncDeclIesKey = struct {
/// null means generic.
cc: ?std.builtin.CallingConvention,
is_var_args: bool,
- is_generic: bool,
is_noinline: bool,
zir_body_inst: TrackedInst.Index,
lbrace_line: u32,
@@ -9564,7 +9054,6 @@ pub fn getFuncDeclIes(
.is_var_args = key.is_var_args,
.has_comptime_bits = key.comptime_bits != 0,
.has_noalias_bits = key.noalias_bits != 0,
- .is_generic = key.is_generic,
.is_noinline = key.is_noinline,
},
});
@@ -9864,7 +9353,6 @@ fn getFuncInstanceIes(
.is_var_args = false,
.has_comptime_bits = false,
.has_noalias_bits = arg.noalias_bits != 0,
- .is_generic = false,
.is_noinline = arg.is_noinline,
},
});
@@ -9972,444 +9460,6 @@ fn finishFuncInstance(
] = @intFromEnum(nav_index);
}
-pub const EnumTypeInit = struct {
- has_values: bool,
- tag_mode: LoadedEnumType.TagMode,
- fields_len: u32,
- key: union(enum) {
- declared: struct {
- zir_index: TrackedInst.Index,
- captures: []const CaptureValue,
- },
- declared_owned_captures: struct {
- zir_index: TrackedInst.Index,
- captures: CaptureValue.Slice,
- },
- reified: struct {
- zir_index: TrackedInst.Index,
- type_hash: u64,
- },
- },
-};
-
-pub const WipEnumType = struct {
- tid: Zcu.PerThread.Id,
- index: Index,
- tag_ty_index: u32,
- type_name_extra_index: u32,
- namespace_extra_index: u32,
- name_nav_extra_index: u32,
- names_map: MapIndex,
- names_start: u32,
- values_map: OptionalMapIndex,
- values_start: u32,
-
- pub fn setName(
- wip: WipEnumType,
- ip: *InternPool,
- type_name: NullTerminatedString,
- /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise.
- name_nav: Nav.Index.Optional,
- ) void {
- const extra = ip.getLocalShared(wip.tid).extra.acquire();
- const extra_items = extra.view().items(.@"0");
- extra_items[wip.type_name_extra_index] = @intFromEnum(type_name);
- extra_items[wip.name_nav_extra_index] = @intFromEnum(name_nav);
- }
-
- pub fn prepare(
- wip: WipEnumType,
- ip: *InternPool,
- namespace: NamespaceIndex,
- ) void {
- const extra = ip.getLocalShared(wip.tid).extra.acquire();
- const extra_items = extra.view().items(.@"0");
-
- extra_items[wip.namespace_extra_index] = @intFromEnum(namespace);
- }
-
- pub fn setTagTy(wip: WipEnumType, ip: *InternPool, tag_ty: Index) void {
- assert(ip.isIntegerType(tag_ty));
- const extra = ip.getLocalShared(wip.tid).extra.acquire();
- extra.view().items(.@"0")[wip.tag_ty_index] = @intFromEnum(tag_ty);
- }
-
- pub const FieldConflict = struct {
- kind: enum { name, value },
- prev_field_idx: u32,
- };
-
- /// Returns the already-existing field with the same name or value, if any.
- /// If the enum is automatially numbered, `value` must be `.none`.
- /// Otherwise, the type of `value` must be the integer tag type of the enum.
- pub fn nextField(wip: WipEnumType, ip: *InternPool, name: NullTerminatedString, value: Index) ?FieldConflict {
- const unwrapped_index = wip.index.unwrap(ip);
- const extra_list = ip.getLocalShared(unwrapped_index.tid).extra.acquire();
- const extra_items = extra_list.view().items(.@"0");
- if (ip.addFieldName(extra_list, wip.names_map, wip.names_start, name)) |conflict| {
- return .{ .kind = .name, .prev_field_idx = conflict };
- }
- if (value == .none) {
- assert(wip.values_map == .none);
- return null;
- }
- assert(ip.typeOf(value) == @as(Index, @enumFromInt(extra_items[wip.tag_ty_index])));
- const map = wip.values_map.unwrap().?.get(ip);
- const field_index = map.count();
- const indexes = extra_items[wip.values_start..][0..field_index];
- const adapter: Index.Adapter = .{ .indexes = @ptrCast(indexes) };
- const gop = map.getOrPutAssumeCapacityAdapted(value, adapter);
- if (gop.found_existing) {
- return .{ .kind = .value, .prev_field_idx = @intCast(gop.index) };
- }
- extra_items[wip.values_start + field_index] = @intFromEnum(value);
- return null;
- }
-
- pub fn cancel(wip: WipEnumType, ip: *InternPool, tid: Zcu.PerThread.Id) void {
- ip.remove(tid, wip.index);
- }
-
- pub const Result = union(enum) {
- wip: WipEnumType,
- existing: Index,
- };
-};
-
-pub fn getEnumType(
- ip: *InternPool,
- gpa: Allocator,
- io: Io,
- tid: Zcu.PerThread.Id,
- ini: EnumTypeInit,
- /// If it is known that there is an existing type with this key which is outdated,
- /// this is passed as `true`, and the type is replaced with one at a fresh index.
- replace_existing: bool,
-) Allocator.Error!WipEnumType.Result {
- const key: Key = .{ .enum_type = switch (ini.key) {
- .declared => |d| .{ .declared = .{
- .zir_index = d.zir_index,
- .captures = .{ .external = d.captures },
- } },
- .declared_owned_captures => |d| .{ .declared = .{
- .zir_index = d.zir_index,
- .captures = .{ .owned = d.captures },
- } },
- .reified => |r| .{ .reified = .{
- .zir_index = r.zir_index,
- .type_hash = r.type_hash,
- } },
- } };
- var gop = if (replace_existing)
- ip.putKeyReplace(io, tid, key)
- else
- try ip.getOrPutKey(gpa, io, tid, key);
- defer gop.deinit();
- if (gop == .existing) return .{ .existing = gop.existing };
-
- const local = ip.getLocal(tid);
- const items = local.getMutableItems(gpa, io);
- try items.ensureUnusedCapacity(1);
- const extra = local.getMutableExtra(gpa, io);
-
- const names_map = try ip.addMap(gpa, io, tid, ini.fields_len);
- errdefer local.mutate.maps.len -= 1;
-
- switch (ini.tag_mode) {
- .auto => {
- assert(!ini.has_values);
- try extra.ensureUnusedCapacity(@typeInfo(EnumAuto).@"struct".fields.len +
- // TODO: fmt bug
- // zig fmt: off
- switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| d.captures.len,
- .reified => 2, // type_hash: PackedU64
- } +
- // zig fmt: on
- ini.fields_len); // field types
-
- const extra_index = addExtraAssumeCapacity(extra, EnumAuto{
- .name = undefined, // set by `prepare`
- .name_nav = undefined, // set by `prepare`
- .captures_len = switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len),
- .reified => std.math.maxInt(u32),
- },
- .namespace = undefined, // set by `prepare`
- .int_tag_type = .none, // set by `prepare`
- .fields_len = ini.fields_len,
- .names_map = names_map,
- .zir_index = switch (ini.key) {
- inline else => |x| x.zir_index,
- }.toOptional(),
- });
- items.appendAssumeCapacity(.{
- .tag = .type_enum_auto,
- .data = extra_index,
- });
- switch (ini.key) {
- .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
- .declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
- .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
- }
- const names_start = extra.mutate.len;
- _ = extra.addManyAsSliceAssumeCapacity(ini.fields_len);
- return .{ .wip = .{
- .tid = tid,
- .index = gop.put(),
- .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?,
- .type_name_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name").?,
- .name_nav_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name_nav").?,
- .namespace_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "namespace").?,
- .names_map = names_map,
- .names_start = @intCast(names_start),
- .values_map = .none,
- .values_start = undefined,
- } };
- },
- .explicit, .nonexhaustive => {
- const values_map: OptionalMapIndex = if (!ini.has_values) .none else m: {
- const values_map = try ip.addMap(gpa, io, tid, ini.fields_len);
- break :m values_map.toOptional();
- };
- errdefer if (ini.has_values) {
- local.mutate.maps.len -= 1;
- };
-
- try extra.ensureUnusedCapacity(@typeInfo(EnumExplicit).@"struct".fields.len +
- // TODO: fmt bug
- // zig fmt: off
- switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| d.captures.len,
- .reified => 2, // type_hash: PackedU64
- } +
- // zig fmt: on
- ini.fields_len + // field types
- ini.fields_len * @intFromBool(ini.has_values)); // field values
-
- const extra_index = addExtraAssumeCapacity(extra, EnumExplicit{
- .name = undefined, // set by `prepare`
- .name_nav = undefined, // set by `prepare`
- .captures_len = switch (ini.key) {
- inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len),
- .reified => std.math.maxInt(u32),
- },
- .namespace = undefined, // set by `prepare`
- .int_tag_type = .none, // set by `prepare`
- .fields_len = ini.fields_len,
- .names_map = names_map,
- .values_map = values_map,
- .zir_index = switch (ini.key) {
- inline else => |x| x.zir_index,
- }.toOptional(),
- });
- items.appendAssumeCapacity(.{
- .tag = switch (ini.tag_mode) {
- .auto => unreachable,
- .explicit => .type_enum_explicit,
- .nonexhaustive => .type_enum_nonexhaustive,
- },
- .data = extra_index,
- });
- switch (ini.key) {
- .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
- .declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
- .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
- }
- const names_start = extra.mutate.len;
- _ = extra.addManyAsSliceAssumeCapacity(ini.fields_len);
- const values_start = extra.mutate.len;
- if (ini.has_values) {
- _ = extra.addManyAsSliceAssumeCapacity(ini.fields_len);
- }
- return .{ .wip = .{
- .tid = tid,
- .index = gop.put(),
- .tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?,
- .type_name_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name").?,
- .name_nav_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name_nav").?,
- .namespace_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "namespace").?,
- .names_map = names_map,
- .names_start = @intCast(names_start),
- .values_map = values_map,
- .values_start = @intCast(values_start),
- } };
- },
- }
-}
-
-const GeneratedTagEnumTypeInit = struct {
- name: NullTerminatedString,
- owner_union_ty: Index,
- tag_ty: Index,
- names: []const NullTerminatedString,
- values: []const Index,
- tag_mode: LoadedEnumType.TagMode,
- parent_namespace: NamespaceIndex,
-};
-
-/// Creates an enum type which was automatically-generated as the tag type of a
-/// `union` with no explicit tag type. Since this is only called once per union
-/// type, it asserts that no matching type yet exists.
-pub fn getGeneratedTagEnumType(
- ip: *InternPool,
- gpa: Allocator,
- io: Io,
- tid: Zcu.PerThread.Id,
- ini: GeneratedTagEnumTypeInit,
-) Allocator.Error!Index {
- assert(ip.isUnion(ini.owner_union_ty));
- assert(ip.isIntegerType(ini.tag_ty));
- for (ini.values) |val| assert(ip.typeOf(val) == ini.tag_ty);
-
- const local = ip.getLocal(tid);
- const items = local.getMutableItems(gpa, io);
- try items.ensureUnusedCapacity(1);
- const extra = local.getMutableExtra(gpa, io);
-
- const names_map = try ip.addMap(gpa, io, tid, ini.names.len);
- errdefer local.mutate.maps.len -= 1;
- ip.addStringsToMap(names_map, ini.names);
-
- const fields_len: u32 = @intCast(ini.names.len);
-
- // Predict the index the enum will live at so we can construct the namespace before releasing the shard's mutex.
- const enum_index = Index.Unwrapped.wrap(.{
- .tid = tid,
- .index = items.mutate.len,
- }, ip);
- const parent_namespace = ip.namespacePtr(ini.parent_namespace);
- const namespace = try ip.createNamespace(gpa, io, tid, .{
- .parent = ini.parent_namespace.toOptional(),
- .owner_type = enum_index,
- .file_scope = parent_namespace.file_scope,
- .generation = parent_namespace.generation,
- });
- errdefer ip.destroyNamespace(tid, namespace);
-
- const prev_extra_len = extra.mutate.len;
- switch (ini.tag_mode) {
- .auto => {
- try extra.ensureUnusedCapacity(@typeInfo(EnumAuto).@"struct".fields.len +
- 1 + // owner_union
- fields_len); // field names
- items.appendAssumeCapacity(.{
- .tag = .type_enum_auto,
- .data = addExtraAssumeCapacity(extra, EnumAuto{
- .name = ini.name,
- .name_nav = .none,
- .captures_len = 0,
- .namespace = namespace,
- .int_tag_type = ini.tag_ty,
- .fields_len = fields_len,
- .names_map = names_map,
- .zir_index = .none,
- }),
- });
- extra.appendAssumeCapacity(.{@intFromEnum(ini.owner_union_ty)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(ini.names)});
- },
- .explicit, .nonexhaustive => {
- try extra.ensureUnusedCapacity(@typeInfo(EnumExplicit).@"struct".fields.len +
- 1 + // owner_union
- fields_len + // field names
- ini.values.len); // field values
-
- const values_map: OptionalMapIndex = if (ini.values.len != 0) m: {
- const map = try ip.addMap(gpa, io, tid, ini.values.len);
- ip.addIndexesToMap(map, ini.values);
- break :m map.toOptional();
- } else .none;
- // We don't clean up the values map on error!
- errdefer @compileError("error path leaks values_map");
-
- items.appendAssumeCapacity(.{
- .tag = switch (ini.tag_mode) {
- .explicit => .type_enum_explicit,
- .nonexhaustive => .type_enum_nonexhaustive,
- .auto => unreachable,
- },
- .data = addExtraAssumeCapacity(extra, EnumExplicit{
- .name = ini.name,
- .name_nav = .none,
- .captures_len = 0,
- .namespace = namespace,
- .int_tag_type = ini.tag_ty,
- .fields_len = fields_len,
- .names_map = names_map,
- .values_map = values_map,
- .zir_index = .none,
- }),
- });
- extra.appendAssumeCapacity(.{@intFromEnum(ini.owner_union_ty)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(ini.names)});
- extra.appendSliceAssumeCapacity(.{@ptrCast(ini.values)});
- },
- }
- errdefer extra.mutate.len = prev_extra_len;
- errdefer switch (ini.tag_mode) {
- .auto => {},
- .explicit, .nonexhaustive => if (ini.values.len != 0) {
- local.mutate.maps.len -= 1;
- },
- };
-
- var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{
- .generated_tag = .{ .union_type = ini.owner_union_ty },
- } });
- defer gop.deinit();
- assert(gop.put() == enum_index);
- return enum_index;
-}
-
-pub const OpaqueTypeInit = struct {
- zir_index: TrackedInst.Index,
- captures: []const CaptureValue,
-};
-
-pub fn getOpaqueType(
- ip: *InternPool,
- gpa: Allocator,
- io: Io,
- tid: Zcu.PerThread.Id,
- ini: OpaqueTypeInit,
-) Allocator.Error!WipNamespaceType.Result {
- var gop = try ip.getOrPutKey(gpa, io, tid, .{ .opaque_type = .{ .declared = .{
- .zir_index = ini.zir_index,
- .captures = .{ .external = ini.captures },
- } } });
- defer gop.deinit();
- if (gop == .existing) return .{ .existing = gop.existing };
-
- const local = ip.getLocal(tid);
- const items = local.getMutableItems(gpa, io);
- const extra = local.getMutableExtra(gpa, io);
- try items.ensureUnusedCapacity(1);
-
- try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + ini.captures.len);
- const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{
- .name = undefined, // set by `finish`
- .name_nav = undefined, // set by `finish`
- .namespace = undefined, // set by `finish`
- .zir_index = ini.zir_index,
- .captures_len = @intCast(ini.captures.len),
- });
- items.appendAssumeCapacity(.{
- .tag = .type_opaque,
- .data = extra_index,
- });
- extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)});
- return .{
- .wip = .{
- .tid = tid,
- .index = gop.put(),
- .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?,
- .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?,
- .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?,
- },
- };
-}
-
pub fn getIfExists(ip: *const InternPool, key: Key) ?Index {
const full_hash = key.hash64(ip);
const hash: u32 = @truncate(full_hash >> 32);
@@ -10534,6 +9584,9 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
TrackedInst.Index,
TrackedInst.Index.Optional,
ComptimeAllocIndex,
+ @FieldType(Tag.TypeStructPacked, "captures_len"),
+ @FieldType(Tag.TypeUnionPacked, "captures_len"),
+ @FieldType(Tag.TypeEnum, "captures_len"),
=> @intFromEnum(@field(item, field.name)),
u32,
@@ -10545,7 +9598,6 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
Tag.TypePointer.PackedOffset,
Tag.TypeUnion.Flags,
Tag.TypeStruct.Flags,
- Tag.TypeStructPacked.Flags,
=> @bitCast(@field(item, field.name)),
else => @compileError("bad field type: " ++ @typeName(field.type)),
@@ -10597,6 +9649,9 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
TrackedInst.Index,
TrackedInst.Index.Optional,
ComptimeAllocIndex,
+ @FieldType(Tag.TypeStructPacked, "captures_len"),
+ @FieldType(Tag.TypeUnionPacked, "captures_len"),
+ @FieldType(Tag.TypeEnum, "captures_len"),
=> @enumFromInt(extra_item),
u32,
@@ -10607,7 +9662,6 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
Tag.TypePointer.PackedOffset,
Tag.TypeUnion.Flags,
Tag.TypeStruct.Flags,
- Tag.TypeStructPacked.Flags,
FuncAnalysis,
=> @bitCast(extra_item),
@@ -10786,7 +9840,7 @@ pub fn getCoerced(
.int => |int| switch (ip.indexToKey(new_ty)) {
.enum_type => return ip.get(gpa, io, tid, .{ .enum_tag = .{
.ty = new_ty,
- .int = try ip.getCoerced(gpa, io, tid, val, ip.loadEnumType(new_ty).tag_ty),
+ .int = try ip.getCoerced(gpa, io, tid, val, ip.loadEnumType(new_ty).int_tag_type),
} }),
.ptr_type => switch (int.storage) {
inline .u64, .i64 => |int_val| return ip.get(gpa, io, tid, .{ .ptr = .{
@@ -10795,7 +9849,6 @@ pub fn getCoerced(
.byte_offset = @intCast(int_val),
} }),
.big_int => unreachable, // must be a usize
- .lazy_align, .lazy_size => {},
},
else => if (ip.isIntegerType(new_ty))
return ip.getCoercedInts(gpa, io, tid, int, new_ty),
@@ -10825,11 +9878,11 @@ pub fn getCoerced(
const index = enum_type.nameIndex(ip, enum_literal).?;
return ip.get(gpa, io, tid, .{ .enum_tag = .{
.ty = new_ty,
- .int = if (enum_type.values.len != 0)
- enum_type.values.get(ip)[index]
+ .int = if (enum_type.field_values.len != 0)
+ enum_type.field_values.get(ip)[index]
else
try ip.get(gpa, io, tid, .{ .int = .{
- .ty = enum_type.tag_ty,
+ .ty = enum_type.int_tag_type,
.storage = .{ .u64 = index },
} }),
} });
@@ -11266,92 +10319,10 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
break :b @sizeOf(Tag.ErrorSet) + (@sizeOf(u32) * info.names_len);
},
.type_inferred_error_set => 0,
- .type_enum_explicit, .type_enum_nonexhaustive => b: {
- const info = extraData(extra_list, EnumExplicit, data);
- var ints = @typeInfo(EnumExplicit).@"struct".fields.len;
- if (info.zir_index == .none) ints += 1;
- ints += if (info.captures_len != std.math.maxInt(u32))
- info.captures_len
- else
- @typeInfo(PackedU64).@"struct".fields.len;
- ints += info.fields_len;
- if (info.values_map != .none) ints += info.fields_len;
- break :b @sizeOf(u32) * ints;
- },
- .type_enum_auto => b: {
- const info = extraData(extra_list, EnumAuto, data);
- const ints = @typeInfo(EnumAuto).@"struct".fields.len + info.captures_len + info.fields_len;
- break :b @sizeOf(u32) * ints;
- },
- .type_opaque => b: {
- const info = extraData(extra_list, Tag.TypeOpaque, data);
- const ints = @typeInfo(Tag.TypeOpaque).@"struct".fields.len + info.captures_len;
- break :b @sizeOf(u32) * ints;
- },
- .type_struct => b: {
- const extra = extraDataTrail(extra_list, Tag.TypeStruct, data);
- const info = extra.data;
- var ints: usize = @typeInfo(Tag.TypeStruct).@"struct".fields.len;
- if (info.flags.any_captures) {
- const captures_len = extra_items[extra.end];
- ints += 1 + captures_len;
- }
- ints += info.fields_len; // types
- ints += 1; // names_map
- ints += info.fields_len; // names
- if (info.flags.any_default_inits)
- ints += info.fields_len; // inits
- if (info.flags.any_aligned_fields)
- ints += (info.fields_len + 3) / 4; // aligns
- if (info.flags.any_comptime_fields)
- ints += (info.fields_len + 31) / 32; // comptime bits
- if (!info.flags.is_extern)
- ints += info.fields_len; // runtime order
- ints += info.fields_len; // offsets
- break :b @sizeOf(u32) * ints;
- },
- .type_struct_packed => b: {
- const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
- const captures_len = if (extra.data.flags.any_captures)
- extra_items[extra.end]
- else
- 0;
- break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).@"struct".fields.len +
- @intFromBool(extra.data.flags.any_captures) + captures_len +
- extra.data.fields_len * 2);
- },
- .type_struct_packed_inits => b: {
- const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
- const captures_len = if (extra.data.flags.any_captures)
- extra_items[extra.end]
- else
- 0;
- break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).@"struct".fields.len +
- @intFromBool(extra.data.flags.any_captures) + captures_len +
- extra.data.fields_len * 3);
- },
.type_tuple => b: {
const info = extraData(extra_list, TypeTuple, data);
break :b @sizeOf(TypeTuple) + (@sizeOf(u32) * 2 * info.fields_len);
},
-
- .type_union => b: {
- const extra = extraDataTrail(extra_list, Tag.TypeUnion, data);
- const captures_len = if (extra.data.flags.any_captures)
- extra_items[extra.end]
- else
- 0;
- const per_field = @sizeOf(u32); // field type
- // 1 byte per field for alignment, rounded up to the nearest 4 bytes
- const alignments = if (extra.data.flags.any_aligned_fields)
- ((extra.data.fields_len + 3) / 4) * 4
- else
- 0;
- break :b @sizeOf(Tag.TypeUnion) +
- 4 * (@intFromBool(extra.data.flags.any_captures) + captures_len) +
- (extra.data.fields_len * per_field) + alignments;
- },
-
.type_function => b: {
const info = extraData(extra_list, Tag.TypeFunction, data);
break :b @sizeOf(Tag.TypeFunction) +
@@ -11360,6 +10331,127 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
(@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits));
},
+ .type_struct => b: {
+ var n: usize = @typeInfo(Tag.TypeStruct).@"struct".fields.len;
+ const extra = extraDataTrail(extra_list, Tag.TypeStruct, data);
+ switch (extra.data.flags.any_captures) {
+ .reified => n += 2, // type_hash: PackedU64
+ .true => {
+ n += 1; // captures_len: u32
+ n += extra_items[extra.end]; // capture: CaptureValue
+ },
+ .false => {},
+ }
+ n += extra.data.fields_len; // field_name: NullTerminatedString
+ n += extra.data.fields_len; // field_type: Index
+ if (extra.data.flags.any_field_defaults) {
+ n += extra.data.fields_len; // field_default: Index
+ }
+ if (extra.data.flags.any_field_aligns) {
+ n += (extra.data.fields_len + 3) / 4; // field_align: Alignment
+ }
+ if (extra.data.flags.any_comptime_fields) {
+ n += (extra.data.fields_len + 31) / 32; // field_is_comptime_bits: u32
+ }
+ if (extra.data.flags.layout == .auto) {
+ n += extra.data.fields_len; // field_runtime_order: RuntimeOrder
+ }
+ n += extra.data.fields_len; // field_offset: u32
+ break :b n * @sizeOf(u32);
+ },
+ .type_struct_packed_auto, .type_struct_packed_explicit => b: {
+ var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".fields.len;
+ const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
+ switch (extra.data.captures_len) {
+ .reified => n += 2, // type_hash: PackedU64
+ _ => |len| n += @intFromEnum(len), // capture: CaptureValue
+ }
+ n += extra.data.fields_len; // field_name: NullTerminatedString
+ n += extra.data.fields_len; // field_type: Index
+ break :b n * @sizeOf(u32);
+ },
+ .type_struct_packed_auto_defaults, .type_struct_packed_explicit_defaults => b: {
+ var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".fields.len;
+ const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
+ switch (extra.data.captures_len) {
+ .reified => n += 2, // type_hash: PackedU64
+ _ => |len| n += @intFromEnum(len), // capture: CaptureValue
+ }
+ n += extra.data.fields_len; // field_name: NullTerminatedString
+ n += extra.data.fields_len; // field_type: Index
+ n += extra.data.fields_len; // field_default: Index
+ break :b n * @sizeOf(u32);
+ },
+ .type_union => b: {
+ var n: usize = @typeInfo(Tag.TypeUnion).@"struct".fields.len;
+ const extra = extraDataTrail(extra_list, Tag.TypeUnion, data);
+ switch (extra.data.flags.any_captures) {
+ .reified => n += 2, // type_hash: PackedU64
+ .true => {
+ n += 1; // captures_len: u32
+ n += extra_items[extra.end]; // capture: CaptureValue
+ },
+ .false => {},
+ }
+ n += extra.data.fields_len; // field_type: Index
+ if (extra.data.flags.any_field_aligns) {
+ n += (extra.data.fields_len + 3) / 4; // field_align: Alignment
+ }
+ break :b n * @sizeOf(u32);
+ },
+ .type_union_packed_auto, .type_union_packed_explicit => b: {
+ var n: usize = @typeInfo(Tag.TypeUnionPacked).@"struct".fields.len;
+ const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data);
+ switch (extra.data.captures_len) {
+ .reified => n += 2, // type_hash: PackedU64
+ _ => |len| n += @intFromEnum(len), // capture: CaptureValue
+ }
+ n += extra.data.fields_len; // field_type: Index
+ break :b n * @sizeOf(u32);
+ },
+ .type_enum_auto => b: {
+ var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len;
+ const extra = extraData(extra_list, Tag.TypeEnum, data);
+ switch (extra.captures_len) {
+ .generated_union_tag => n += 1, // owner_union: Index
+ .reified => {
+ n += 1; // zir_index: TrackedInst.Index,
+ n += 2; // type_hash: PackedU64
+ },
+ _ => |len| {
+ n += 1; // zir_index: TrackedInst.Index,
+ n += @intFromEnum(len); // capture: CaptureValue
+ },
+ }
+ n += extra.fields_len; // field_name: NullTerminatedString
+ break :b n * @sizeOf(u32);
+ },
+ .type_enum_explicit, .type_enum_nonexhaustive => b: {
+ var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len;
+ const extra = extraData(extra_list, Tag.TypeEnum, data);
+ switch (extra.captures_len) {
+ .generated_union_tag => n += 1, // owner_union: Index
+ .reified => {
+ n += 1; // zir_index: TrackedInst.Index,
+ n += 2; // type_hash: PackedU64
+ },
+ _ => |len| {
+ n += 1; // zir_index: TrackedInst.Index,
+ n += @intFromEnum(len); // capture: CaptureValue
+ },
+ }
+ n += 1; // field_value_map: MapIndex
+ n += extra.fields_len; // field_name: NullTerminatedString
+ n += extra.fields_len; // field_value: Index
+ break :b n * @sizeOf(u32);
+ },
+ .type_opaque => b: {
+ var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len;
+ const extra = extraData(extra_list, Tag.TypeOpaque, data);
+ n += extra.captures_len; // capture: CaptureValue
+ break :b n * @sizeOf(u32);
+ },
+
.undef => 0,
.simple_type => 0,
.simple_value => 0,
@@ -11393,8 +10485,6 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
break :b @sizeOf(Int) + int.limbs_len * @sizeOf(Limb);
},
- .int_lazy_align, .int_lazy_size => @sizeOf(IntLazy),
-
.error_set_error, .error_union_error => @sizeOf(Key.Error),
.error_union_payload => @sizeOf(Tag.TypeValue),
.enum_literal => 0,
@@ -11484,16 +10574,20 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
.type_anyerror_union,
.type_error_set,
.type_inferred_error_set,
+ .type_tuple,
+ .type_function,
+ .type_struct,
+ .type_struct_packed_auto,
+ .type_struct_packed_explicit,
+ .type_struct_packed_auto_defaults,
+ .type_struct_packed_explicit_defaults,
+ .type_union,
+ .type_union_packed_auto,
+ .type_union_packed_explicit,
+ .type_enum_auto,
.type_enum_explicit,
.type_enum_nonexhaustive,
- .type_enum_auto,
.type_opaque,
- .type_struct,
- .type_struct_packed,
- .type_struct_packed_inits,
- .type_tuple,
- .type_union,
- .type_function,
.undef,
.ptr_nav,
.ptr_comptime_alloc,
@@ -11517,8 +10611,6 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
.int_small,
.int_positive,
.int_negative,
- .int_lazy_align,
- .int_lazy_size,
.error_set_error,
.error_union_error,
.error_union_payload,
@@ -12245,16 +11337,20 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.type_anyerror_union,
.type_error_set,
.type_inferred_error_set,
+ .type_tuple,
+ .type_function,
+ .type_struct,
+ .type_struct_packed_auto,
+ .type_struct_packed_explicit,
+ .type_struct_packed_auto_defaults,
+ .type_struct_packed_explicit_defaults,
+ .type_union,
+ .type_union_packed_auto,
+ .type_union_packed_explicit,
.type_enum_auto,
.type_enum_explicit,
.type_enum_nonexhaustive,
.type_opaque,
- .type_struct,
- .type_struct_packed,
- .type_struct_packed_inits,
- .type_tuple,
- .type_union,
- .type_function,
=> .type_type,
.undef,
@@ -12278,8 +11374,6 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.opt_payload,
.error_union_payload,
.int_small,
- .int_lazy_align,
- .int_lazy_size,
.error_set_error,
.error_union_error,
.enum_tag,
@@ -12613,22 +11707,26 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.type_inferred_error_set,
=> .error_set,
+ .simple_type => unreachable, // handled via Index tag above
+
+ .type_tuple => .@"struct",
+
+ .type_struct,
+ .type_struct_packed_auto,
+ .type_struct_packed_explicit,
+ .type_struct_packed_auto_defaults,
+ .type_struct_packed_explicit_defaults,
+ => .@"struct",
+ .type_union,
+ .type_union_packed_auto,
+ .type_union_packed_explicit,
+ => .@"union",
.type_enum_auto,
.type_enum_explicit,
.type_enum_nonexhaustive,
=> .@"enum",
-
- .simple_type => unreachable, // handled via Index tag above
-
- .type_opaque => .@"opaque",
-
- .type_struct,
- .type_struct_packed,
- .type_struct_packed_inits,
- .type_tuple,
- => .@"struct",
-
- .type_union => .@"union",
+ .type_opaque,
+ => .@"opaque",
.type_function => .@"fn",
@@ -12658,8 +11756,6 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.int_small,
.int_positive,
.int_negative,
- .int_lazy_align,
- .int_lazy_size,
.error_set_error,
.error_union_error,
.error_union_payload,
@@ -13169,3 +12265,113 @@ const PackedCallingConvention = packed struct(u18) {
};
}
};
+
+/// Asserts that `struct_type` is a non-packed struct type.
+/// As well as calling this function, the caller must also populate these arrays:
+/// * `field_types`
+/// * `field_aligns`
+/// * `field_runtime_order`
+/// * `field_offsets`
+pub fn resolveStructLayout(
+ ip: *InternPool,
+ io: Io,
+ struct_type: Index,
+ size: u32,
+ alignment: Alignment,
+ has_no_possible_value: bool,
+ has_one_possible_value: bool,
+ comptime_only: bool,
+) void {
+ const unwrapped_index = struct_type.unwrap(ip);
+
+ const local = ip.getLocal(unwrapped_index.tid);
+ local.mutate.extra.mutex.lockUncancelable(io);
+ defer local.mutate.extra.mutex.unlock(io);
+
+ const extra_items = local.shared.extra.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ assert(item.tag == .type_struct);
+
+ extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "size").?] = size;
+ const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?]);
+ flags.has_no_possible_value = has_no_possible_value;
+ flags.has_one_possible_value = has_one_possible_value;
+ flags.comptime_only = comptime_only;
+ flags.alignment = alignment;
+}
+
+/// Asserts that `union_type` is a non-packed union type.
+/// As well as calling this function, the caller must also populate these arrays:
+/// * `field_types`
+/// * `field_aligns`
+pub fn resolveUnionLayout(
+ ip: *InternPool,
+ io: Io,
+ union_type: Index,
+ size: u32,
+ padding: u32,
+ alignment: Alignment,
+ has_no_possible_value: bool,
+ has_one_possible_value: bool,
+ comptime_only: bool,
+) void {
+ const unwrapped_index = union_type.unwrap(ip);
+
+ const local = ip.getLocal(unwrapped_index.tid);
+ local.mutate.extra.mutex.lockUncancelable(io);
+ defer local.mutate.extra.mutex.unlock(io);
+
+ const extra_items = local.shared.extra.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ assert(item.tag == .type_union);
+
+ extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "size").?] = size;
+ extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "padding").?] = padding;
+ const flags: *Tag.TypeUnion.Flags = @ptrCast(&extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "flags").?]);
+ flags.has_no_possible_value = has_no_possible_value;
+ flags.has_one_possible_value = has_one_possible_value;
+ flags.comptime_only = comptime_only;
+ flags.alignment = alignment;
+}
+
+/// Asserts that `struct_type` is a packed struct type.
+pub fn resolvePackedStructBackingInt(ip: *InternPool, io: Io, struct_type: Index, backing_int_type: Index) void {
+ const unwrapped_index = struct_type.unwrap(ip);
+
+ const local = ip.getLocal(unwrapped_index.tid);
+ local.mutate.extra.mutex.lockUncancelable(io);
+ defer local.mutate.extra.mutex.unlock(io);
+
+ const extra_items = local.shared.extra.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ switch (item.tag) {
+ .type_struct_packed_auto,
+ .type_struct_packed_explicit,
+ .type_struct_packed_auto_defaults,
+ .type_struct_packed_explicit_defaults,
+ => {},
+ else => unreachable,
+ }
+
+ extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "backing_int_type").?] = @intFromEnum(backing_int_type);
+}
+
+/// Asserts that `union_type` is a packed union type.
+pub fn resolvePackedUnionBackingInt(ip: *InternPool, io: Io, union_type: Index, backing_int_type: Index) void {
+ const unwrapped_index = union_type.unwrap(ip);
+
+ const local = ip.getLocal(unwrapped_index.tid);
+ local.mutate.extra.mutex.lockUncancelable(io);
+ defer local.mutate.extra.mutex.unlock(io);
+
+ const extra_items = local.shared.extra.view().items(.@"0");
+ const item = unwrapped_index.getItem(ip);
+ switch (item.tag) {
+ .type_union_packed_auto,
+ .type_union_packed_explicit,
+ => {},
+ else => unreachable,
+ }
+
+ extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "backing_int_type").?] = @intFromEnum(backing_int_type);
+}
diff --git a/src/Sema.zig b/src/Sema.zig
index 58fa1af124239ff05093bc9cf857a16f36de09e4..55fb718a45b989ecac821626638dde9a98ab99b5 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -173,13 +173,17 @@ const ComptimeAlloc = struct {
runtime_index: RuntimeIndex,
};
+/// Asserts that `ty` is not an OPV type.
/// `src` may be `null` if `is_const` will be set.
fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex {
const pt = sema.pt;
- const init_val = try sema.typeHasOnePossibleValue(ty) orelse try pt.undefValue(ty);
+
+ // Explicit guard because this call mutates the InternPool so cannot be optimized out.
+ if (std.debug.runtime_safety) assert(ty.onePossibleValue(pt) catch @panic("") == null);
+
const idx = sema.comptime_allocs.items.len;
try sema.comptime_allocs.append(sema.gpa, .{
- .val = .{ .interned = init_val.toIntern() },
+ .val = .{ .interned = (try pt.undefValue(ty)).toIntern() },
.is_const = false,
.src = src,
.alignment = alignment,
@@ -1382,10 +1386,10 @@ fn analyzeBodyInner(
const extended = datas[@intFromEnum(inst)].extended;
break :ext switch (extended.opcode) {
// zig fmt: off
- .struct_decl => try sema.zirStructDecl( block, extended, inst),
- .enum_decl => try sema.zirEnumDecl( block, extended, inst),
- .union_decl => try sema.zirUnionDecl( block, extended, inst),
- .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst),
+ .struct_decl => try sema.zirStructDecl( block, inst),
+ .enum_decl => try sema.zirEnumDecl( block, inst),
+ .union_decl => try sema.zirUnionDecl( block, inst),
+ .opaque_decl => try sema.zirOpaqueDecl( block, inst),
.tuple_decl => try sema.zirTupleDecl( block, extended),
.this => try sema.zirThis( block, extended),
.ret_addr => try sema.zirRetAddr( block, extended),
@@ -1993,6 +1997,24 @@ fn analyzeBodyInner(
assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()));
break;
}
+ //
+ if (air_inst.toIndex()) |air_inst_index| {
+ switch (sema.air_instructions.items(.tag)[@intFromEnum(air_inst_index)]) {
+ .inferred_alloc, .inferred_alloc_comptime => {},
+ else => {
+ assert(sema.typeOf(air_inst).onePossibleValue(pt) catch @panic("") == null);
+ sema.typeOf(air_inst).assertHasLayout(zcu);
+ },
+ }
+ } else {
+ switch (tags[@intFromEnum(inst)]) {
+ // MLUGG TODO: do we actually *want* this exception? we could arguably simplify things without it
+ // e.g. analyzeNavVal could stop doing ensureLayoutResolved in most cases (`extern` is an exception) and instead do `assertHasLayout`
+ .func, .func_inferred, .func_fancy => {}, // exception: we're in a func decl, layout will get resolved in a bit by `analyzeNavVal`
+ else => sema.typeOf(air_inst).assertHasLayout(zcu),
+ }
+ }
+ //
map.putAssumeCapacity(inst, air_inst);
i += 1;
}
@@ -2190,7 +2212,7 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi
}
}
-fn analyzeAsType(
+pub fn analyzeAsType(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -2227,7 +2249,6 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
// var st: StackTrace = undefined;
const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
- try stack_trace_ty.resolveFields(pt);
const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty));
// st.instruction_addresses = &addrs;
@@ -2247,14 +2268,11 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
}
/// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
-fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
+/// TODO MLUGG: remove the error union return!
+fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) error{}!?Value {
const zcu = sema.pt.zcu;
assert(inst != .none);
- if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
- return opv;
- }
-
if (inst.toInterned()) |ip_index| {
const val: Value = .fromInterned(ip_index);
assert(val.getVariable(zcu) == null);
@@ -2267,12 +2285,18 @@ fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
.inferred_alloc_comptime => unreachable, // assertion failure
else => {},
}
+ // Assert that the type is not OPV -- if it was, the value would have been comptime-known.
+ // Explicit guard because this could add to the InternPool so cannot be optimized away.
+ if (std.debug.runtime_safety) {
+ const opv = sema.typeOf(inst).onePossibleValue(sema.pt) catch @panic("oom in assert");
+ assert(opv == null);
+ }
return null;
}
}
/// Like `resolveValue`, but emits an error if the value is not comptime-known.
-fn resolveConstValue(
+pub fn resolveConstValue(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -2301,7 +2325,7 @@ fn resolveDefinedValue(
}
/// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined.
-fn resolveConstDefinedValue(
+pub fn resolveConstDefinedValue(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -2315,11 +2339,6 @@ fn resolveConstDefinedValue(
return val;
}
-/// Like `resolveValue`, but recursively resolves lazy values before returning.
-fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
- return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null);
-}
-
/// Value Tag may be `undef` or `variable`.
pub fn resolveFinalDeclValue(
sema: *Sema,
@@ -2439,13 +2458,14 @@ fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non
fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
const pt = sema.pt;
+ const zcu = pt.zcu;
const msg = msg: {
const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{
ty.fmt(pt),
});
errdefer msg.destroy(sema.gpa);
- if (ty.isSlice(pt.zcu)) {
- try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.elemType2(pt.zcu).fmt(pt)});
+ if (ty.isSlice(zcu)) {
+ try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.childType(zcu).fmt(pt)});
}
break :msg msg;
};
@@ -2644,7 +2664,7 @@ pub fn fail(
src: LazySrcLoc,
comptime format: []const u8,
args: anytype,
-) CompileError {
+) SemaError {
const err_msg = try sema.errMsg(src, format, args);
inline for (args) |arg| {
if (@TypeOf(arg) == Type.Formatter) {
@@ -2798,27 +2818,26 @@ fn analyzeAsInt(
) !u64 {
const coerced = try sema.coerce(block, dest_ty, air_ref, src);
const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
- return try val.toUnsignedIntSema(sema.pt);
+ return val.toUnsignedInt(sema.pt.zcu);
}
fn analyzeValueAsCallconv(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- unresolved_val: Value,
+ val: Value,
) !std.builtin.CallingConvention {
- return interpretBuiltinType(sema, block, src, unresolved_val, std.builtin.CallingConvention);
+ return interpretBuiltinType(sema, block, src, val, std.builtin.CallingConvention);
}
fn interpretBuiltinType(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- unresolved_val: Value,
+ val: Value,
comptime T: type,
) !T {
- const resolved_val = try sema.resolveLazyValue(unresolved_val);
- return resolved_val.interpret(T, sema.pt) catch |err| switch (err) {
+ return val.interpret(T, sema.pt) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.UndefinedValue => return sema.failWithUseOfUndef(block, src, null),
error.TypeMismatch => @panic("std.builtin is corrupt"),
@@ -2913,7 +2932,13 @@ fn validateTupleFieldType(
/// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,
/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.
-fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {
+fn getCaptures(
+ sema: *Sema,
+ block: *Block,
+ type_src: LazySrcLoc,
+ zir_captures: []const Zir.Inst.Capture,
+ zir_capture_names: []const Zir.NullTerminatedString,
+) ![]InternPool.CaptureValue {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
@@ -2924,41 +2949,38 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type);
const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu);
- const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len);
+ const captures = try sema.arena.alloc(InternPool.CaptureValue, zir_captures.len);
- for (sema.code.extra[extra_index..][0..captures_len], sema.code.extra[extra_index + captures_len ..][0..captures_len], captures) |raw, raw_name, *capture| {
- const zir_capture: Zir.Inst.Capture = @bitCast(raw);
- const zir_name: Zir.NullTerminatedString = @enumFromInt(raw_name);
+ for (zir_captures, zir_capture_names, captures) |zir_capture, zir_name, *capture| {
const zir_name_slice = sema.code.nullTerminatedString(zir_name);
capture.* = switch (zir_capture.unwrap()) {
.nested => |parent_idx| parent_captures.get(ip)[parent_idx],
- .instruction_load => |ptr_inst| InternPool.CaptureValue.wrap(capture: {
+ .instruction_load => |ptr_inst| capture: {
const ptr_ref = try sema.resolveInst(ptr_inst.toRef());
const ptr_val = try sema.resolveValue(ptr_ref) orelse {
- break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() };
+ break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() });
};
// TODO: better source location
- const unresolved_loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse {
- break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() };
+ const loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse {
+ break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() });
};
- const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val);
if (loaded_val.canMutateComptimeVarState(zcu)) {
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val);
}
- break :capture .{ .@"comptime" = loaded_val.toIntern() };
- }),
- .instruction => |inst| InternPool.CaptureValue.wrap(capture: {
+ break :capture .wrap(.{ .@"comptime" = loaded_val.toIntern() });
+ },
+ .instruction => |inst| capture: {
const air_ref = try sema.resolveInst(inst.toRef());
- if (try sema.resolveValueResolveLazy(air_ref)) |val| {
+ if (try sema.resolveValue(air_ref)) |val| {
if (val.canMutateComptimeVarState(zcu)) {
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val);
}
- break :capture .{ .@"comptime" = val.toIntern() };
+ break :capture .wrap(.{ .@"comptime" = val.toIntern() });
}
- break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() };
- }),
+ break :capture .wrap(.{ .runtime = sema.typeOf(air_ref).toIntern() });
+ },
.decl_val => |str| capture: {
const decl_name = try ip.getOrPutString(
gpa,
@@ -2968,7 +2990,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
.no_embedded_nulls,
);
const nav = try sema.lookupIdentifier(block, decl_name);
- break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav });
+ break :capture .wrap(.{ .nav_val = nav });
},
.decl_ref => |str| capture: {
const decl_name = try ip.getOrPutString(
@@ -2987,621 +3009,6 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
return captures;
}
-fn zirStructDecl(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand);
-
- const tracked_inst = try block.trackZir(inst);
- const src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = LazySrcLoc.Offset.nodeOffset(.zero),
- };
-
- var extra_index = extra.end;
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
-
- const captures = try sema.getCaptures(block, src, extra_index, captures_len);
- extra_index += captures_len * 2;
-
- if (small.has_backing_int) {
- const backing_int_body_len = sema.code.extra[extra_index];
- extra_index += 1; // backing_int_body_len
- if (backing_int_body_len == 0) {
- extra_index += 1; // backing_int_ref
- } else {
- extra_index += backing_int_body_len; // backing_int_body_inst
- }
- }
-
- const struct_init: InternPool.StructTypeInit = .{
- .layout = small.layout,
- .fields_len = fields_len,
- .known_non_opv = small.known_non_opv,
- .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
- .any_comptime_fields = small.any_comptime_fields,
- .any_default_inits = small.any_default_inits,
- .inits_resolved = false,
- .any_aligned_fields = small.any_aligned_fields,
- .key = .{ .declared = .{
- .zir_index = tracked_inst,
- .captures = captures,
- } },
- };
- const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, struct_init, false)) {
- .existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty);
-
- // Make sure we update the namespace if the declaration is re-analyzed, to pick
- // up on e.g. changed comptime decls.
- try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
-
- try sema.declareDependency(.{ .interned = new_ty });
- try sema.addTypeReferenceEntry(src, new_ty);
- return Air.internedToRef(new_ty);
- },
- .wip => |wip| wip,
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- const type_name = try sema.createTypeName(
- block,
- small.name_strategy,
- "struct",
- inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
-
- const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .owner_type = wip_ty.index,
- .file_scope = block.getFileScopeIndex(zcu),
- .generation = zcu.generation,
- });
- errdefer pt.destroyNamespace(new_namespace_index);
-
- if (pt.zcu.comp.config.incremental) {
- try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
- }
-
- const decls = sema.code.bodySlice(extra_index, decls_len);
- try pt.scanNamespace(new_namespace_index, decls);
-
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- codegen_type: {
- if (zcu.comp.config.use_llvm) break :codegen_type;
- if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
- try sema.declareDependency(.{ .interned = wip_ty.index });
- try sema.addTypeReferenceEntry(src, wip_ty.index);
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
-}
-
-pub fn createTypeName(
- sema: *Sema,
- block: *Block,
- name_strategy: Zir.Inst.NameStrategy,
- anon_prefix: []const u8,
- inst: ?Zir.Inst.Index,
- /// This is used purely to give the type a unique name in the `anon` case.
- type_index: InternPool.Index,
-) CompileError!struct {
- name: InternPool.NullTerminatedString,
- nav: InternPool.Nav.Index.Optional,
-} {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- switch (name_strategy) {
- .anon => {}, // handled after switch
- .parent => return .{
- .name = block.type_name_ctx,
- .nav = sema.owner.unwrap().nav_val.toOptional(),
- },
- .func => func_strat: {
- const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail);
- const zir_tags = sema.code.instructions.items(.tag);
-
- var aw: std.Io.Writer.Allocating = .init(gpa);
- defer aw.deinit();
- const w = &aw.writer;
- w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
-
- var arg_i: usize = 0;
- for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
- .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
- const arg = sema.inst_map.get(zir_inst).?;
- // If this is being called in a generic function then analyzeCall will
- // have already resolved the args and this will work.
- // If not then this is a struct type being returned from a non-generic
- // function and the name doesn't matter since it will later
- // result in a compile error.
- const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat
-
- if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory;
-
- // Limiting the depth here helps avoid type names getting too long, which
- // in turn helps to avoid unreasonably long symbol names for namespaced
- // symbols. Such names should ideally be human-readable, and additionally,
- // some tooling may not support very long symbol names.
- w.print("{f}", .{Value.fmtValueSemaFull(.{
- .val = arg_val,
- .pt = pt,
- .opt_sema = sema,
- .depth = 1,
- })}) catch return error.OutOfMemory;
-
- arg_i += 1;
- continue;
- },
- else => continue,
- };
-
- w.writeByte(')') catch return error.OutOfMemory;
- return .{
- .name = try ip.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls),
- .nav = .none,
- };
- },
- .dbg_var => {
- // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions.
- const ref = inst.?.toRef();
- const zir_tags = sema.code.instructions.items(.tag);
- const zir_data = sema.code.instructions.items(.data);
- for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) {
- .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) {
- return .{
- .name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{
- block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code),
- }, .no_embedded_nulls),
- .nav = .none,
- };
- },
- else => {},
- };
- // fall through to anon strat
- },
- }
-
- // anon strat handling
-
- // It would be neat to have "struct:line:column" but this name has
- // to survive incremental updates, where it may have been shifted down
- // or up to a different line, but unchanged, and thus not unnecessarily
- // semantically analyzed.
- // TODO: that would be possible, by detecting line number changes and renaming
- // types appropriately. However, `@typeName` becomes a problem then. If we remove
- // that builtin from the language, we can consider this.
-
- return .{
- .name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}__{s}_{d}", .{
- block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index),
- }, .no_embedded_nulls),
- .nav = .none,
- };
-}
-
-fn zirEnumDecl(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
-
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
- const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand);
- var extra_index: usize = extra.end;
-
- const tracked_inst = try block.trackZir(inst);
- const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
-
- const tag_type_ref = if (small.has_tag_type) blk: {
- const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
- extra_index += 1;
- break :blk tag_type_ref;
- } else .none;
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
-
- const body_len = if (small.has_body_len) blk: {
- const body_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
-
- const captures = try sema.getCaptures(block, src, extra_index, captures_len);
- extra_index += captures_len * 2;
-
- const decls = sema.code.bodySlice(extra_index, decls_len);
- extra_index += decls_len;
-
- const body = sema.code.bodySlice(extra_index, body_len);
- extra_index += body.len;
-
- const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
- const body_end = extra_index;
- extra_index += bit_bags_count;
-
- const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
- if (bag != 0) break true;
- } else false;
-
- const enum_init: InternPool.EnumTypeInit = .{
- .has_values = any_values,
- .tag_mode = if (small.nonexhaustive)
- .nonexhaustive
- else if (tag_type_ref == .none)
- .auto
- else
- .explicit,
- .fields_len = fields_len,
- .key = .{ .declared = .{
- .zir_index = tracked_inst,
- .captures = captures,
- } },
- };
- const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, enum_init, false)) {
- .existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty);
-
- // Make sure we update the namespace if the declaration is re-analyzed, to pick
- // up on e.g. changed comptime decls.
- try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
-
- try sema.declareDependency(.{ .interned = new_ty });
- try sema.addTypeReferenceEntry(src, new_ty);
-
- // Since this is an enum, it has to be resolved immediately.
- // `ensureTypeUpToDate` has resolved the new type if necessary.
- // We just need to check for resolution failures.
- const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
- if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
- return error.AnalysisFail;
- }
-
- return Air.internedToRef(new_ty);
- },
- .wip => |wip| wip,
- };
-
- // Once this is `true`, we will not delete the decl or type even upon failure, since we
- // have finished constructing the type and are in the process of analyzing it.
- var done = false;
-
- errdefer if (!done) wip_ty.cancel(ip, pt.tid);
-
- const type_name = try sema.createTypeName(
- block,
- small.name_strategy,
- "enum",
- inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
-
- const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .owner_type = wip_ty.index,
- .file_scope = block.getFileScopeIndex(zcu),
- .generation = zcu.generation,
- });
- errdefer if (!done) pt.destroyNamespace(new_namespace_index);
-
- try pt.scanNamespace(new_namespace_index, decls);
-
- try sema.declareDependency(.{ .interned = wip_ty.index });
- try sema.addTypeReferenceEntry(src, wip_ty.index);
-
- // We've finished the initial construction of this type, and are about to perform analysis.
- // Set the namespace appropriately, and don't destroy anything on failure.
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- wip_ty.prepare(ip, new_namespace_index);
- done = true;
-
- {
- const tracked_unit = zcu.trackUnitSema(type_name.name.toSlice(ip), null);
- defer tracked_unit.end(zcu);
- try Sema.resolveDeclaredEnum(
- pt,
- wip_ty,
- inst,
- tracked_inst,
- new_namespace_index,
- type_name.name,
- small,
- body,
- tag_type_ref,
- any_values,
- fields_len,
- sema.code,
- body_end,
- );
- }
-
- codegen_type: {
- if (zcu.comp.config.use_llvm) break :codegen_type;
- if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
- return Air.internedToRef(wip_ty.index);
-}
-
-fn zirUnionDecl(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
-
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
- const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand);
- var extra_index: usize = extra.end;
-
- const tracked_inst = try block.trackZir(inst);
- const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
-
- extra_index += @intFromBool(small.has_tag_type);
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- extra_index += @intFromBool(small.has_body_len);
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
-
- const captures = try sema.getCaptures(block, src, extra_index, captures_len);
- extra_index += captures_len * 2;
-
- const union_init: InternPool.UnionTypeInit = .{
- .flags = .{
- .layout = small.layout,
- .status = .none,
- .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
- .tagged
- else if (small.layout != .auto)
- .none
- else switch (block.wantSafeTypes()) {
- true => .safety,
- false => .none,
- },
- .any_aligned_fields = small.any_aligned_fields,
- .requires_comptime = .unknown,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
- .alignment = .none,
- },
- .fields_len = fields_len,
- .enum_tag_ty = .none, // set later
- .field_types = &.{}, // set later
- .field_aligns = &.{}, // set later
- .key = .{ .declared = .{
- .zir_index = tracked_inst,
- .captures = captures,
- } },
- };
- const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, union_init, false)) {
- .existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty);
-
- // Make sure we update the namespace if the declaration is re-analyzed, to pick
- // up on e.g. changed comptime decls.
- try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
-
- try sema.declareDependency(.{ .interned = new_ty });
- try sema.addTypeReferenceEntry(src, new_ty);
- return Air.internedToRef(new_ty);
- },
- .wip => |wip| wip,
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- const type_name = try sema.createTypeName(
- block,
- small.name_strategy,
- "union",
- inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
-
- const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .owner_type = wip_ty.index,
- .file_scope = block.getFileScopeIndex(zcu),
- .generation = zcu.generation,
- });
- errdefer pt.destroyNamespace(new_namespace_index);
-
- if (pt.zcu.comp.config.incremental) {
- try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
- }
-
- const decls = sema.code.bodySlice(extra_index, decls_len);
- try pt.scanNamespace(new_namespace_index, decls);
-
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- codegen_type: {
- if (zcu.comp.config.use_llvm) break :codegen_type;
- if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
- try sema.declareDependency(.{ .interned = wip_ty.index });
- try sema.addTypeReferenceEntry(src, wip_ty.index);
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
-}
-
-fn zirOpaqueDecl(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
-
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
- const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand);
- var extra_index: usize = extra.end;
-
- const tracked_inst = try block.trackZir(inst);
- const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = sema.code.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
-
- const captures = try sema.getCaptures(block, src, extra_index, captures_len);
- extra_index += captures_len * 2;
-
- const opaque_init: InternPool.OpaqueTypeInit = .{
- .zir_index = tracked_inst,
- .captures = captures,
- };
- const wip_ty = switch (try ip.getOpaqueType(gpa, io, pt.tid, opaque_init)) {
- .existing => |ty| {
- // Make sure we update the namespace if the declaration is re-analyzed, to pick
- // up on e.g. changed comptime decls.
- try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(zcu));
-
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
- },
- .wip => |wip| wip,
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- const type_name = try sema.createTypeName(
- block,
- small.name_strategy,
- "opaque",
- inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
-
- const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .owner_type = wip_ty.index,
- .file_scope = block.getFileScopeIndex(zcu),
- .generation = zcu.generation,
- });
- errdefer pt.destroyNamespace(new_namespace_index);
-
- const decls = sema.code.bodySlice(extra_index, decls_len);
- try pt.scanNamespace(new_namespace_index, decls);
-
- codegen_type: {
- if (zcu.comp.config.use_llvm) break :codegen_type;
- if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
- try sema.addTypeReferenceEntry(src, wip_ty.index);
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
-}
-
fn zirErrorSetDecl(
sema: *Sema,
inst: Zir.Inst.Index,
@@ -3640,16 +3047,16 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
defer tracy.end();
const pt = sema.pt;
+ const zcu = pt.zcu;
const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node);
- if (block.isComptime() or try sema.fn_ret_ty.comptimeOnlySema(pt)) {
- try sema.fn_ret_ty.resolveFields(pt);
+ if (block.isComptime() or sema.fn_ret_ty.comptimeOnly(zcu)) {
return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none);
}
- const target = pt.zcu.getTarget();
- const ptr_type = try pt.ptrTypeSema(.{
+ const target = zcu.getTarget();
+ const ptr_type = try pt.ptrType(.{
.child = sema.fn_ret_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -3826,6 +3233,7 @@ fn zirAllocExtended(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
+ const zcu = pt.zcu;
const gpa = sema.gpa;
const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
const var_src = block.nodeOffset(extra.data.src_node);
@@ -3847,37 +3255,20 @@ fn zirAllocExtended(
break :blk try sema.resolveAlign(block, align_src, align_ref);
} else .none;
- if (block.isComptime() or small.is_comptime) {
- if (small.has_type) {
- return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
- } else {
- try sema.air_instructions.append(gpa, .{
- .tag = .inferred_alloc_comptime,
- .data = .{ .inferred_alloc_comptime = .{
- .alignment = alignment,
- .is_const = small.is_const,
- .ptr = undefined,
- } },
- });
- return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
- }
- }
-
- if (small.has_type and try var_ty.comptimeOnlySema(pt)) {
- return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
- }
-
if (small.has_type) {
+ try sema.ensureLayoutResolved(var_ty);
+ if (block.isComptime() or small.is_comptime or var_ty.comptimeOnly(zcu)) {
+ return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
+ }
if (!small.is_const) {
try sema.validateVarType(block, ty_src, var_ty, false);
}
const target = pt.zcu.getTarget();
- try var_ty.resolveLayout(pt);
- if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
+ if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) {
const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node });
return sema.fail(block, store_src, "local variable in naked function", .{});
}
- const ptr_type = try sema.pt.ptrTypeSema(.{
+ const ptr_type = try pt.ptrType(.{
.child = var_ty.toIntern(),
.flags = .{
.alignment = alignment,
@@ -3893,6 +3284,19 @@ fn zirAllocExtended(
return ptr;
}
+ if (block.isComptime() or small.is_comptime) {
+ const iac_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
+ try sema.air_instructions.append(gpa, .{
+ .tag = .inferred_alloc_comptime,
+ .data = .{ .inferred_alloc_comptime = .{
+ .alignment = alignment,
+ .is_const = small.is_const,
+ .ptr = undefined,
+ } },
+ });
+ return iac_index.toRef();
+ }
+
const result_index = try block.addInstAsIndex(.{
.tag = .inferred_alloc,
.data = .{ .inferred_alloc = .{
@@ -3916,6 +3320,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
const var_src = block.nodeOffset(inst_data.src_node);
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
+ try sema.ensureLayoutResolved(var_ty);
return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
}
@@ -3978,7 +3383,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
return sema.makePtrConst(block, Air.internedToRef(ptr_val));
}
- if (try elem_ty.comptimeOnlySema(pt)) {
+ if (elem_ty.comptimeOnly(zcu)) {
// The value was initialized through RLS, so we didn't detect the runtime condition earlier.
// TODO: source location of runtime control flow
const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
@@ -4001,20 +3406,23 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
const ptr_info = alloc_ty.ptrInfo(zcu);
const elem_ty: Type = .fromInterned(ptr_info.child);
+ elem_ty.assertHasLayout(zcu);
const alloc_inst = alloc.toIndex() orelse return null;
const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null;
const stores = comptime_info.value.stores.items(.inst);
+ // If the elem type is OPV, no need to faff about with `stores`; just use the OPV.
+ if (try elem_ty.onePossibleValue(pt)) |opv| {
+ return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, opv.toIntern(), null, alloc_inst, comptime_info.value);
+ }
+
+ // Since the elem type isn't OPV, there should have been at least one store.
+ assert(stores.len > 0);
+
// Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known.
// We will resolve and return its value.
- // We expect to have emitted at least one store, unless the elem type is OPV.
- if (stores.len == 0) {
- const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern();
- return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value);
- }
-
// In general, we want to create a comptime alloc of the correct type and
// apply the stores to that alloc in order. However, before going to all
// that effort, let's optimize for the common case of a single store.
@@ -4118,7 +3526,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
const idx_val = (try sema.resolveValue(data.rhs)).?;
break :blk .{
data.lhs,
- .{ .elem = try idx_val.toUnsignedIntSema(pt) },
+ .{ .elem = idx_val.toUnsignedInt(zcu) },
};
},
.bitcast => .{
@@ -4150,7 +3558,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
// If the payload is OPV, we must use that value instead of undef.
const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
const payload_ty = opt_ty.optionalChild(zcu);
- const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
+ const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty);
const opt_val = try pt.intern(.{ .opt = .{
.ty = opt_ty.toIntern(),
.val = payload_val.toIntern(),
@@ -4163,7 +3571,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
// If the payload is OPV, we must use that value instead of undef.
const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
const payload_ty = eu_ty.errorUnionPayload(zcu);
- const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
+ const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty);
const eu_val = try pt.intern(.{ .error_union = .{
.ty = eu_ty.toIntern(),
.val = .{ .payload = payload_val.toIntern() },
@@ -4178,7 +3586,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
// The payload value will be stored later, so undef is a sufficent payload for now.
const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);
const payload_val = try pt.undefValue(payload_ty);
- const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx);
+ const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx);
const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
}
@@ -4207,7 +3615,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?);
const union_ty = union_ptr_val.typeOf(zcu).childType(zcu);
const field_ty = union_ty.unionFieldType(tag_val, zcu).?;
- if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| {
+ if (try field_ty.onePossibleValue(pt)) |payload_val| {
const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val);
try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty);
}
@@ -4289,7 +3697,7 @@ fn finishResolveComptimeKnownAllocPtr(
fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type {
var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu);
ptr_info.flags.is_const = true;
- return sema.pt.ptrTypeSema(ptr_info);
+ return sema.pt.ptrType(ptr_info);
}
fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref {
@@ -4326,21 +3734,23 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
defer tracy.end();
const pt = sema.pt;
+ const zcu = pt.zcu;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
const var_src = block.nodeOffset(inst_data.src_node);
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
- if (block.isComptime() or try var_ty.comptimeOnlySema(pt)) {
+ try sema.ensureLayoutResolved(var_ty);
+ if (block.isComptime() or var_ty.comptimeOnly(zcu)) {
return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
}
- if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
+ if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) {
const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
return sema.fail(block, mut_src, "local variable in naked function", .{});
}
- const target = pt.zcu.getTarget();
- const ptr_type = try pt.ptrTypeSema(.{
+ const target = zcu.getTarget();
+ const ptr_type = try pt.ptrType(.{
.child = var_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -4356,21 +3766,24 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
defer tracy.end();
const pt = sema.pt;
+ const zcu = pt.zcu;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
const var_src = block.nodeOffset(inst_data.src_node);
+
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
+ try sema.ensureLayoutResolved(var_ty);
if (block.isComptime()) {
return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
}
- if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
+ if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) {
const store_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
return sema.fail(block, store_src, "local variable in naked function", .{});
}
try sema.validateVarType(block, ty_src, var_ty, false);
- const target = pt.zcu.getTarget();
- const ptr_type = try pt.ptrTypeSema(.{
+ const target = zcu.getTarget();
+ const ptr_type = try pt.ptrType(.{
.child = var_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -4430,8 +3843,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
.inferred_alloc_comptime => {
- // The work was already done for us by `Sema.storeToInferredAllocComptime`.
- // All we need to do is return the pointer.
+ // The work was already done for us by `Sema.storeToInferredAllocComptime`. Also, since
+ // we had a value of the exact correct type to store, the result type's layout must be
+ // already resolved. So all we need to do here is return the pointer.
const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
const resolved_ptr = iac.ptr;
@@ -4450,7 +3864,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
};
if (zcu.intern_pool.isFuncBody(val)) {
const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
- if (try ty.fnHasRuntimeBitsSema(pt)) {
+ if (ty.fnHasRuntimeBits(zcu)) {
const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val);
try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
@@ -4469,8 +3883,10 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
peer_val.* = bin_op.rhs;
}
const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none);
+ // The layout of the peers is already resolved, so the layout of `final_elem_ty` is too.
+ final_elem_ty.assertHasLayout(zcu);
- const final_ptr_ty = try pt.ptrTypeSema(.{
+ const final_ptr_ty = try pt.ptrType(.{
.child = final_elem_ty.toIntern(),
.flags = .{
.alignment = ia1.alignment,
@@ -4484,21 +3900,16 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty);
const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty);
- // Unless the block is comptime, `alloc_inferred` always produces
- // a runtime constant. The final inferred type needs to be
- // fully resolved so it can be lowered in codegen.
- try final_elem_ty.resolveFully(pt);
-
return Air.internedToRef(new_const_ptr.toIntern());
}
- if (try final_elem_ty.comptimeOnlySema(pt)) {
+ if (final_elem_ty.comptimeOnly(zcu)) {
// The alloc wasn't comptime-known per the above logic, so the
// type cannot be comptime-only.
// TODO: source location of runtime control flow
return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)});
}
- if (sema.func_is_naked and try final_elem_ty.hasRuntimeBitsSema(pt)) {
+ if (sema.func_is_naked and final_elem_ty.hasRuntimeBits(zcu)) {
const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
return sema.fail(block, mut_src, "local variable in naked function", .{});
}
@@ -4812,7 +4223,7 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
if (is_ref) {
var ptr_info = operand_ty.ptrInfo(zcu);
ptr_info.child = eu_ty.toIntern();
- const eu_ptr_ty = try pt.ptrTypeSema(ptr_info);
+ const eu_ptr_ty = try pt.ptrType(ptr_info);
return Air.internedToRef(eu_ptr_ty.toIntern());
} else {
return Air.internedToRef(eu_ty.toIntern());
@@ -4935,7 +4346,6 @@ fn validateArrayInitTy(
return;
},
.@"struct" => if (ty.isTuple(zcu)) {
- try ty.resolveFields(pt);
const array_len = ty.arrayLen(zcu);
if (init_count > array_len) {
return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
@@ -5097,12 +4507,16 @@ fn validateStructInit(
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
for (found_fields, 0..) |explicit, i_usize| {
+ const i: u32 = @intCast(i_usize);
+
if (explicit) continue;
- const i: u32 = @intCast(i_usize);
+ if (struct_ty.structFieldIsComptime(i, zcu)) continue;
- try struct_ty.resolveStructFieldInits(pt);
- const default_val = struct_ty.structFieldDefaultValue(i, zcu);
- if (default_val.toIntern() == .unreachable_value) {
+ if (!struct_ty.isTuple(zcu)) {
+ try sema.ensureFieldInitsResolved(struct_ty);
+ }
+
+ const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse {
const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
const template = "missing tuple field with index {d}";
if (root_msg) |msg| {
@@ -5120,7 +4534,7 @@ fn validateStructInit(
root_msg = try sema.errMsg(init_src, template, args);
}
continue;
- }
+ };
const field_src = init_src; // TODO better source location
const default_field_ptr = if (struct_ty.isTuple(zcu))
@@ -5166,11 +4580,9 @@ fn zirValidatePtrArrayInit(
var root_msg: ?*Zcu.ErrorMsg = null;
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
- try array_ty.resolveStructFieldInits(pt);
var i = instrs.len;
while (i < array_len) : (i += 1) {
- const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern();
- if (default_val == .unreachable_value) {
+ if (array_ty.structFieldDefaultValue(i, zcu) == null) {
const template = "missing tuple field with index {d}";
if (root_msg) |msg| {
try sema.errNote(init_src, msg, template, .{i});
@@ -5224,17 +4636,19 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
.slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}),
}
- if ((try sema.typeHasOnePossibleValue(operand_ty.childType(zcu))) != null) {
+ const elem_ty = operand_ty.childType(zcu);
+ try sema.ensureLayoutResolved(elem_ty);
+
+ if (try elem_ty.onePossibleValue(pt) != null) {
// No need to validate the actual pointer value, we don't need it!
return;
}
- const elem_ty = operand_ty.elemType2(zcu);
if (try sema.resolveValue(operand)) |val| {
if (val.isUndef(zcu)) {
return sema.fail(block, src, "cannot dereference undefined value", .{});
}
- } else if (try elem_ty.comptimeOnlySema(pt)) {
+ } else if (elem_ty.comptimeOnly(zcu)) {
const msg = msg: {
const msg = try sema.errMsg(
src,
@@ -5373,7 +4787,7 @@ fn failWithBadUnionFieldAccess(
return sema.failWithOwnedErrorMsg(block, msg);
}
-fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void {
+pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void {
const zcu = sema.pt.zcu;
const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
const category = switch (decl_ty.zigTypeTag(zcu)) {
@@ -5443,14 +4857,16 @@ fn storeToInferredAllocComptime(
const operand_val = try sema.resolveValue(operand) orelse {
return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var });
};
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = operand_ty.toIntern(),
.flags = .{
.alignment = iac.alignment,
.is_const = iac.is_const,
},
});
- if (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)) {
+ if (try operand_ty.onePossibleValue(pt) != null or
+ (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)))
+ {
iac.ptr = try pt.intern(.{ .ptr = .{
.ty = alloc_ty.toIntern(),
.base_addr = .{ .uav = .{
@@ -5624,7 +5040,7 @@ fn zirCompileLog(
const arg = try sema.resolveInst(arg_ref);
const arg_ty = sema.typeOf(arg);
- if (try sema.resolveValueResolveLazy(arg)) |val| {
+ if (try sema.resolveValue(arg)) |val| {
writer.print("@as({f}, {f})", .{
arg_ty.fmt(pt), val.fmtValueSema(pt, sema),
}) catch return error.OutOfMemory;
@@ -5928,10 +5344,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
try pt.ensureFileAnalyzed(new_file_index);
- const ty = zcu.fileRootType(new_file_index);
- try sema.declareDependency(.{ .interned = ty });
+ const ty: Type = .fromInterned(zcu.fileRootType(new_file_index));
try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
+ return .fromType(ty);
}
fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -6177,10 +5592,11 @@ fn resolveAnalyzedBlock(
// to emit a jump instruction to after the block when it encounters the break.
try parent_block.instructions.append(gpa, merges.block_inst);
const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items });
+ resolved_ty.assertHasLayout(zcu);
// TODO add note "missing else causes void value"
const type_src = src; // TODO: better source location
- if (try resolved_ty.comptimeOnlySema(pt)) {
+ if (resolved_ty.comptimeOnly(zcu)) {
const msg = msg: {
const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)});
errdefer msg.destroy(sema.gpa);
@@ -6274,10 +5690,7 @@ fn resolveAnalyzedBlock(
});
}
- if (try sema.typeHasOnePossibleValue(resolved_ty)) |block_only_value| {
- return Air.internedToRef(block_only_value.toIntern());
- }
-
+ if (try resolved_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
return merges.block_inst.toRef();
}
@@ -6413,7 +5826,8 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
.@"comptime",
.nav_val,
.nav_ty,
- .type,
+ .type_layout,
+ .type_inits,
.memoized_state,
=> return, // does nothing outside a function
};
@@ -6431,7 +5845,8 @@ fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
.@"comptime",
.nav_val,
.nav_ty,
- .type,
+ .type_layout,
+ .type_inits,
.memoized_state,
=> return, // does nothing outside a function
};
@@ -6589,8 +6004,8 @@ fn addDbgVar(
.dbg_var_val, .dbg_arg_inline => operand_ty,
else => unreachable,
};
- if (try val_ty.comptimeOnlySema(pt)) return;
- if (!(try val_ty.hasRuntimeBitsSema(pt))) return;
+ if (val_ty.comptimeOnly(zcu)) return;
+ if (!val_ty.hasRuntimeBits(zcu)) return;
if (try sema.resolveValue(operand)) |operand_val| {
if (operand_val.canMutateComptimeVarState(zcu)) return;
}
@@ -6759,7 +6174,6 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref
if (!block.ownerModule().error_tracing) return .none;
const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
- try stack_trace_ty.resolveFields(pt);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {
error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"),
@@ -6803,7 +6217,6 @@ fn popErrorReturnTrace(
// the result is comptime-known to be a non-error. Either way, pop unconditionally.
const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
- try stack_trace_ty.resolveFields(pt);
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);
@@ -6829,7 +6242,6 @@ fn popErrorReturnTrace(
// If non-error, then pop the error return trace by restoring the index.
const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
- try stack_trace_ty.resolveFields(pt);
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);
@@ -6969,7 +6381,6 @@ fn zirCall(
// need to clean-up our own trace if we were passed to a non-error-handling expression.
if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) {
const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace);
- try stack_trace_ty.resolveFields(pt);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
@@ -7318,6 +6729,8 @@ fn analyzeCall(
} else func_src;
const func_ty_info = zcu.typeToFunc(func_ty).?;
+ // MLUGG TODO: this isn't quite the check i want. this includes inline functions, which aren't *generic*...
+ const func_is_generic = !func_ty.fnHasRuntimeBits(zcu);
if (!callConvIsCallable(func_ty_info.cc)) {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(
@@ -7353,7 +6766,7 @@ fn analyzeCall(
else => unreachable,
} else .{ null, false };
- if (func_ty_info.is_generic and func_val == null) {
+ if (func_is_generic and func_val == null) {
return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target });
}
@@ -7369,19 +6782,18 @@ fn analyzeCall(
.src = call_src,
.r = .{ .simple = .comptime_call_modifier },
} };
- } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) {
- block.comptime_reason = .{
- .reason = .{
+ } else if (!inline_requested) {
+ const ret_ty: Type = .fromInterned(func_ty_info.return_type);
+ if (ret_ty.comptimeOnly(zcu)) {
+ block.comptime_reason = .{ .reason = .{
.src = call_src,
- .r = .{
- .comptime_only_ret_ty = .{
- .ty = .fromInterned(func_ty_info.return_type),
- .is_generic_inst = false,
- .ret_ty_src = func_ret_ty_src,
- },
- },
- },
- };
+ .r = .{ .comptime_only_ret_ty = .{
+ .ty = .fromInterned(func_ty_info.return_type),
+ .is_generic_inst = false,
+ .ret_ty_src = func_ret_ty_src,
+ } },
+ } };
+ }
}
}
@@ -7403,13 +6815,13 @@ fn analyzeCall(
// This is the `inst_map` used when evaluating generic parameters and return types.
var generic_inst_map: InstMap = .{};
defer generic_inst_map.deinit(gpa);
- if (func_ty_info.is_generic) {
+ if (func_is_generic) {
try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
}
// This exists so that `generic_block` below can include a "called from here" note back to this
// call site when analyzing generic parameter/return types.
- var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{
+ var generic_inlining: Block.Inlining = if (func_is_generic) .{
.call_block = block,
.call_src = call_src,
.func = func_val.?.toIntern(),
@@ -7422,7 +6834,7 @@ fn analyzeCall(
// This is the block in which we evaluate generic function components: that is, generic parameter
// types and the generic return type. This must not be used if the function is not generic.
// `comptime_reason` is set as needed.
- var generic_block: Block = if (func_ty_info.is_generic) .{
+ var generic_block: Block = if (func_is_generic) .{
.parent = null,
.sema = sema,
.namespace = fn_nav.analysis.?.namespace,
@@ -7431,9 +6843,9 @@ fn analyzeCall(
.src_base_inst = fn_nav.analysis.?.zir_index,
.type_name_ctx = fn_nav.fqn,
} else undefined;
- defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa);
+ defer if (func_is_generic) generic_block.instructions.deinit(gpa);
- if (func_ty_info.is_generic) {
+ if (func_is_generic) {
// We certainly depend on the generic owner's signature!
try sema.declareDependency(.{ .src_hash = fn_tracked_inst });
}
@@ -7445,7 +6857,7 @@ fn analyzeCall(
if (raw != .generic_poison_type) break :ty .fromInterned(raw);
// We must discover the generic parameter type.
- assert(func_ty_info.is_generic);
+ assert(func_is_generic);
const param_inst_idx = fn_zir_info.param_body[arg_idx];
const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx));
switch (param_inst.tag) {
@@ -7494,11 +6906,11 @@ fn analyzeCall(
return arg.*; // terminate analysis here
}
- if (func_ty_info.is_generic) {
+ if (func_is_generic) {
// We need to put the argument into `generic_inst_map` so that other parameters can refer to it.
const param_inst_idx = fn_zir_info.param_body[arg_idx];
const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false;
- const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt);
+ const param_is_comptime = declared_comptime or arg_ty.comptimeOnly(zcu);
// We allow comptime-known arguments to propagate to generic types not only for comptime
// parameters, but if the call is known to be inline.
if (param_is_comptime or early_known_inline) {
@@ -7516,6 +6928,10 @@ fn analyzeCall(
);
}
generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*);
+ } else if (try arg_ty.onePossibleValue(pt)) |opv| {
+ // The argument is comptime-known, even though this is a generic instantiation (as
+ // opposed to an inline call), because the parameter type is OPV.
+ generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, .fromValue(opv));
} else {
// We need a dummy instruction with this type. It doesn't actually need to be in any block,
// since it will never be referenced at runtime!
@@ -7532,7 +6948,7 @@ fn analyzeCall(
// calls (where it should be the IES of the instantiation). However, it's how we print this
// in error messages.
const resolved_ret_ty: Type = ret_ty: {
- if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type);
+ if (!func_is_generic) break :ret_ty .fromInterned(func_ty_info.return_type);
const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: {
break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type);
@@ -7542,7 +6958,7 @@ fn analyzeCall(
// Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`.
- assert(func_ty_info.is_generic);
+ assert(func_is_generic);
const old_code = sema.code;
const old_inst_map = sema.inst_map;
@@ -7584,10 +7000,11 @@ fn analyzeCall(
break :ret_ty full_ty;
};
+ try sema.ensureLayoutResolved(resolved_ret_ty);
// If we've discovered after evaluating arguments that a generic function instantiation is
// comptime-only, then we can mark the block as comptime *now*.
- if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) {
+ if (!inline_requested and !block.isComptime() and resolved_ret_ty.comptimeOnly(zcu)) {
block.comptime_reason = .{
.reason = .{
.src = call_src,
@@ -7618,7 +7035,7 @@ fn analyzeCall(
});
if (func_ty_info.cc == .auto) {
switch (sema.owner.unwrap()) {
- .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
+ .@"comptime", .nav_ty, .nav_val, .type_layout, .type_inits, .memoized_state => {},
.func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true),
}
}
@@ -7626,7 +7043,7 @@ fn analyzeCall(
try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg);
}
const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: {
- if (!func_ty_info.is_generic) break :func .{ callee, args };
+ if (!func_is_generic) break :func .{ callee, args };
// Instantiate the generic function!
@@ -7648,7 +7065,7 @@ fn analyzeCall(
break :c true;
}
}
- break :c try arg_ty.comptimeOnlySema(pt);
+ break :c arg_ty.comptimeOnly(zcu);
};
const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false;
@@ -7680,6 +7097,7 @@ fn analyzeCall(
.generic_owner = func_val.?.toIntern(),
.comptime_args = comptime_args,
});
+ try sema.ensureLayoutResolved(.fromInterned(ip.typeOf(func_instance)));
if (zcu.comp.debugIncremental()) {
const nav = ip.indexToKey(func_instance).func.owner_nav;
const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav);
@@ -7753,12 +7171,12 @@ fn analyzeCall(
return .unreachable_value;
}
- const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv|
- .fromValue(opv)
- else
- maybe_opv;
-
- return result;
+ try sema.ensureLayoutResolved(sema.typeOf(maybe_opv));
+ if (try sema.typeOf(maybe_opv).onePossibleValue(pt)) |opv| {
+ return .fromValue(opv);
+ } else {
+ return maybe_opv;
+ }
}
// This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.
@@ -7824,6 +7242,11 @@ fn analyzeCall(
}
}
+ // We're about to do an inline call; if the return type expression was generic, the return type
+ // may not be resolved yet. It's correct to resolve it because the function is going to return a
+ // value of this type.
+ try sema.ensureLayoutResolved(resolved_ret_ty);
+
// For an inline call, we depend on the source code of the whole function definition.
try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
@@ -8000,6 +7423,10 @@ fn analyzeCall(
break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);
};
+ if (sema.typeOf(result_raw).isNoReturn(zcu)) {
+ return .unreachable_value;
+ }
+
const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: {
const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
break :r Air.internedToRef(val_resolved);
@@ -8080,16 +7507,16 @@ fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
const zcu = pt.zcu;
const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type;
+ try sema.ensureLayoutResolved(maybe_wrapped_indexable_ty);
const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu);
- try indexable_ty.resolveFields(pt);
assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction
- if (indexable_ty.zigTypeTag(zcu) == .@"struct") {
- const elem_type = indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu);
- return Air.internedToRef(elem_type.toIntern());
- } else {
- const elem_type = indexable_ty.elemType2(zcu);
- return Air.internedToRef(elem_type.toIntern());
- }
+ const elem_ty = switch (indexable_ty.zigTypeTag(zcu)) {
+ .@"struct" => indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu),
+ .array, .vector => indexable_ty.childType(zcu),
+ .pointer => indexable_ty.indexablePtrElem(zcu),
+ else => unreachable,
+ };
+ return .fromType(elem_ty);
}
fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -8355,7 +7782,7 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src);
if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
- const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt));
+ const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(zcu));
if (int > len: {
const mutate = &ip.global_error_set.mutate;
mutate.map.mutex.lockUncancelable(io);
@@ -8539,7 +7966,6 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) {
.@"enum" => operand,
.@"union" => blk: {
- try operand_ty.resolveFields(pt);
const tag_ty = operand_ty.unionTagType(zcu) orelse {
return sema.fail(
block,
@@ -8568,17 +7994,9 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
});
}
- if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| {
- return Air.internedToRef((try pt.getCoerced(opv, int_tag_ty)).toIntern());
- }
-
if (try sema.resolveValue(enum_tag)) |enum_tag_val| {
- if (enum_tag_val.isUndef(zcu)) {
- return pt.undefRef(int_tag_ty);
- }
-
- const val = try enum_tag_val.intFromEnum(enum_tag_ty, pt);
- return Air.internedToRef(val.toIntern());
+ if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty);
+ return .fromValue(enum_tag_val.intFromEnum(zcu));
}
try sema.requireRuntimeBlock(block, src, operand_src);
@@ -8626,19 +8044,15 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum });
}
- if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| {
+ if (try dest_ty.onePossibleValue(pt)) |opv| {
if (block.wantSafety()) {
// The operand is runtime-known but the result is comptime-known. In
// this case we still need a safety check.
- const expect_int_val = switch (zcu.intern_pool.indexToKey(opv.toIntern())) {
- .enum_tag => |enum_tag| enum_tag.int,
- else => unreachable,
- };
- const expect_int_coerced = try pt.getCoerced(.fromInterned(expect_int_val), operand_ty);
- const ok = try block.addBinOp(.cmp_eq, operand, Air.internedToRef(expect_int_coerced.toIntern()));
+ const expect_int = try pt.getCoerced(opv.intFromEnum(zcu), operand_ty);
+ const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));
try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
}
- return Air.internedToRef(opv.toIntern());
+ return .fromValue(opv);
}
try sema.requireRuntimeBlock(block, src, operand_src);
@@ -8666,6 +8080,7 @@ fn zirOptionalPayloadPtr(
return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
}
+/// MLUGG TODO: pre-resolved child?
fn analyzeOptionalPayloadPtr(
sema: *Sema,
block: *Block,
@@ -8685,7 +8100,8 @@ fn analyzeOptionalPayloadPtr(
}
const child_type = opt_type.optionalChild(zcu);
- const child_pointer = try pt.ptrTypeSema(.{
+ try sema.ensureLayoutResolved(child_type);
+ const child_pointer = try pt.ptrType(.{
.child = child_type.toIntern(),
.flags = .{
.is_const = optional_ptr_ty.isConstPtr(zcu),
@@ -8698,7 +8114,7 @@ fn analyzeOptionalPayloadPtr(
if (sema.isComptimeMutablePtr(ptr_val)) {
// Set the optional to non-null at comptime.
// If the payload is OPV, we must use that value instead of undef.
- const payload_val = try sema.typeHasOnePossibleValue(child_type) orelse try pt.undefValue(child_type);
+ const payload_val = try child_type.onePossibleValue(pt) orelse try pt.undefValue(child_type);
const opt_val = try pt.intern(.{ .opt = .{
.ty = opt_type.toIntern(),
.val = payload_val.toIntern(),
@@ -8759,7 +8175,7 @@ fn zirOptionalPayload(
// 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.ptrTypeSema(.{
+ break :t try pt.ptrType(.{
.child = ptr_info.child,
.flags = .{
.alignment = ptr_info.flags.alignment,
@@ -8784,11 +8200,14 @@ fn zirOptionalPayload(
return .unreachable_value;
}
- try sema.requireRuntimeBlock(block, src, null);
if (safety_check and block.wantSafety()) {
const is_non_null = try block.addUnOp(.is_non_null, operand);
try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
}
+
+ // If the payload is OPV, we need the safety check but have a comptime-known result.
+ if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
+
return block.addTyOp(.optional_payload, result_ty, operand);
}
@@ -8844,8 +8263,8 @@ fn analyzeErrUnionPayload(
try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
}
- if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_only_value| {
- return Air.internedToRef(payload_only_value.toIntern());
+ if (try payload_ty.onePossibleValue(pt)) |payload_opv| {
+ return .fromValue(payload_opv);
}
return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
@@ -8867,6 +8286,7 @@ fn zirErrUnionPayloadPtr(
return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
}
+/// MLUGG TODO LAYOUT: already-resolved child?
fn analyzeErrUnionPayloadPtr(
sema: *Sema,
block: *Block,
@@ -8888,7 +8308,8 @@ fn analyzeErrUnionPayloadPtr(
const err_union_ty = operand_ty.childType(zcu);
const payload_ty = err_union_ty.errorUnionPayload(zcu);
- const operand_pointer_ty = try pt.ptrTypeSema(.{
+ try sema.ensureLayoutResolved(payload_ty);
+ const operand_pointer_ty = try pt.ptrType(.{
.child = payload_ty.toIntern(),
.flags = .{
.is_const = operand_ty.isConstPtr(zcu),
@@ -8901,7 +8322,7 @@ fn analyzeErrUnionPayloadPtr(
if (sema.isComptimeMutablePtr(ptr_val)) {
// Set the error union to non-error at comptime.
// If the payload is OPV, we must use that value instead of undef.
- const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
+ const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty);
const eu_val = try pt.intern(.{ .error_union = .{
.ty = err_union_ty.toIntern(),
.val = .{ .payload = payload_val.toIntern() },
@@ -9571,10 +8992,6 @@ fn funcCommon(
const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
- const func_src = block.nodeOffset(src_node_offset);
-
- const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt);
- var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime;
var comptime_bits: u32 = 0;
for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| {
@@ -9587,11 +9004,7 @@ fn funcCommon(
.fn_proto_node_offset = src_node_offset,
.param_index = @intCast(i),
} });
- const param_ty_comptime = try param_ty.comptimeOnlySema(pt);
const param_ty_generic = param_ty.isGenericPoison();
- if (param_is_comptime or param_ty_comptime or param_ty_generic) {
- is_generic = true;
- }
if (param_is_comptime) {
comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error
}
@@ -9609,24 +9022,6 @@ fn funcCommon(
param_src,
cc,
);
- if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) {
- const msg = msg: {
- const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{
- param_ty.fmt(pt),
- });
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsComptime(msg, param_src, param_ty);
-
- try sema.addDeclaredHereNote(msg, param_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- }
-
- if (var_args and is_generic) {
- return sema.fail(block, func_src, "generic function cannot be variadic", .{});
}
try sema.checkReturnTypeAndCallConvCommon(
@@ -9643,46 +9038,6 @@ fn funcCommon(
is_noinline,
);
- // If the return type is comptime-only but not dependent on parameters then
- // all parameter types also need to be comptime.
- if (has_body and ret_ty_requires_comptime and !block.isComptime()) comptime_check: {
- for (block.params.items(.is_comptime)) |is_comptime| {
- if (!is_comptime) break;
- } else break :comptime_check;
- const ies_ret_ty_prefix: []const u8 = if (inferred_error_set) "!" else "";
- const msg = try sema.errMsg(
- ret_ty_src,
- "function with comptime-only return type '{s}{f}' requires all parameters to be comptime",
- .{ ies_ret_ty_prefix, bare_return_type.fmt(pt) },
- );
- errdefer msg.destroy(sema.gpa);
- try sema.explainWhyTypeIsComptime(msg, ret_ty_src, bare_return_type);
-
- const tags = sema.code.instructions.items(.tag);
- const data = sema.code.instructions.items(.data);
- const param_body = sema.code.getParamBody(func_inst);
- for (
- block.params.items(.is_comptime),
- block.params.items(.name),
- param_body[0..block.params.len],
- ) |is_comptime, name_nts, param_index| {
- if (!is_comptime) {
- const param_src = block.tokenOffset(switch (tags[@intFromEnum(param_index)]) {
- .param => data[@intFromEnum(param_index)].pl_tok.src_tok,
- .param_anytype => data[@intFromEnum(param_index)].str_tok.src_tok,
- else => unreachable,
- });
- const name = sema.code.nullTerminatedString(name_nts);
- if (name.len != 0) {
- try sema.errNote(param_src, msg, "param '{s}' is required to be comptime", .{name});
- } else {
- try sema.errNote(param_src, msg, "param is required to be comptime", .{});
- }
- }
- }
- return sema.failWithOwnedErrorMsg(block, msg);
- }
-
const param_types = block.params.items(.ty);
if (inferred_error_set) {
@@ -9696,7 +9051,6 @@ fn funcCommon(
.bare_return_type = bare_return_type.toIntern(),
.cc = cc,
.is_var_args = var_args,
- .is_generic = is_generic,
.is_noinline = is_noinline,
.zir_body_inst = try block.trackZir(func_inst),
@@ -9714,7 +9068,6 @@ fn funcCommon(
.return_type = bare_return_type.toIntern(),
.cc = cc,
.is_var_args = var_args,
- .is_generic = is_generic,
.is_noinline = is_noinline,
});
@@ -9845,16 +9198,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
if (!ptr_ty.isPtrAtRuntime(zcu)) {
return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)});
}
- const pointee_ty = ptr_ty.childType(zcu);
- if (try ptr_ty.comptimeOnlySema(pt)) {
- const msg = msg: {
- const msg = try sema.errMsg(ptr_src, "comptime-only type '{f}' has no pointer address", .{pointee_ty.fmt(pt)});
- errdefer msg.destroy(sema.gpa);
- try sema.explainWhyTypeIsComptime(msg, ptr_src, pointee_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
+
const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize;
@@ -9863,7 +9207,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
if (operand_val.isUndef(zcu)) {
return .undef_usize;
}
- const addr = try operand_val.getUnsignedIntSema(pt) orelse {
+ const addr = operand_val.getUnsignedInt(zcu) orelse {
// Wasn't an integer pointer. This is a runtime operation.
break :ct;
};
@@ -9879,7 +9223,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
new_elem.* = .undef_usize;
continue;
}
- const addr = try ptr_val.getUnsignedIntSema(pt) orelse {
+ const addr = ptr_val.getUnsignedInt(zcu) orelse {
// A vector element wasn't an integer pointer. This is a runtime operation.
break :ct;
};
@@ -10044,7 +9388,7 @@ fn intCast(
try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
- if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| {
+ if (try dest_ty.onePossibleValue(pt)) |opv| {
// requirement: intCast(u0, input) iff input == 0
if (block.wantSafety()) {
try sema.requireRuntimeBlock(block, src, operand_src);
@@ -10382,6 +9726,8 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
};
return sema.failWithOwnedErrorMsg(block, msg);
}
+ try sema.checkIndexable(block, src, indexable_ty);
+ try sema.ensureLayoutResolved(indexable_ty.childType(zcu));
return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
}
@@ -10764,7 +10110,8 @@ fn analyzeSwitchBlock(
.{ raw_operand, .none };
const operand_ty = sema.typeOf(val);
- const maybe_operand_opv = try sema.typeHasOnePossibleValue(operand_ty);
+ operand_ty.assertHasLayout(zcu);
+ const maybe_operand_opv = try operand_ty.onePossibleValue(pt);
const init_cond: Air.Inst.Ref, const item_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
.@"union" => tag: {
const tag_ty = operand_ty.unionTagType(zcu).?;
@@ -10776,6 +10123,7 @@ fn analyzeSwitchBlock(
operand_ty,
},
};
+ item_ty.assertHasLayout(zcu);
if (zir_switch.has_continue and !block.isComptime()) {
const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and
@@ -10881,7 +10229,7 @@ fn analyzeSwitchBlock(
unreachable;
}
- if (try sema.typeHasOnePossibleValue(item_ty)) |item_opv| {
+ if (try item_ty.onePossibleValue(pt)) |item_opv| {
// We simplify conditions with OPV to either a `loop` or a `block` since
// we cannot switch on a value which doesn't exist at runtime.
assert(operand == .loop); // `simple` should have already been comptime-resolved above!
@@ -11249,8 +10597,8 @@ fn finishSwitchBr(
var item = sema.resolveConstDefinedValue(block, .unneeded, range_ref[0], undefined) catch unreachable;
const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_ref[1], undefined) catch unreachable;
- if (try item.getUnsignedIntSema(pt)) |first_int| {
- if (try item_last.getUnsignedIntSema(pt)) |last_int| {
+ if (item.getUnsignedInt(zcu)) |first_int| {
+ if (item_last.getUnsignedInt(zcu)) |last_int| {
if (std.math.cast(u32, last_int - first_int)) |range_len| {
try branch_hints.ensureUnusedCapacity(gpa, range_len);
}
@@ -11259,7 +10607,6 @@ fn finishSwitchBr(
var prev_result_overflowed = false;
while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
- // Previous validation has resolved any possible lazy values.
const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
.int => .{ item, operand_ty },
.@"enum" => b: {
@@ -11896,72 +11243,68 @@ fn validateSwitchBlock(
try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
}
- const operand_ty: Type, const item_ty: Type = check_operand: {
- const operand_ty = operand_ty: {
- const raw_operand_ty = sema.typeOf(raw_operand);
- if (operand_is_ref) {
- try sema.checkPtrType(block, operand_src, raw_operand_ty, false);
- break :operand_ty raw_operand_ty.childType(zcu);
- }
- break :operand_ty raw_operand_ty;
- };
-
- const item_ty: Type = item_ty: {
- switch (operand_ty.zigTypeTag(zcu)) {
- .@"enum",
- .error_set,
- .int,
- .comptime_int,
- .type,
- .enum_literal,
- .@"fn",
- .bool,
- .void,
- => break :item_ty operand_ty,
+ const operand_ty = operand_ty: {
+ const raw_operand_ty = sema.typeOf(raw_operand);
+ if (operand_is_ref) {
+ try sema.checkPtrType(block, operand_src, raw_operand_ty, false);
+ break :operand_ty raw_operand_ty.childType(zcu);
+ }
+ break :operand_ty raw_operand_ty;
+ };
+ try sema.ensureLayoutResolved(operand_ty);
- .@"union" => {
- try operand_ty.resolveFields(pt);
- const enum_ty = operand_ty.unionTagType(zcu) orelse {
- return sema.failWithOwnedErrorMsg(block, msg: {
- const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{});
- errdefer msg.destroy(sema.gpa);
- if (operand_ty.srcLocOrNull(zcu)) |union_src| {
- try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
- }
- break :msg msg;
- });
- };
- break :item_ty enum_ty;
- },
+ const item_ty: Type = item_ty: {
+ switch (operand_ty.zigTypeTag(zcu)) {
+ .@"enum",
+ .error_set,
+ .int,
+ .comptime_int,
+ .type,
+ .enum_literal,
+ .@"fn",
+ .bool,
+ .void,
+ => break :item_ty operand_ty,
- .pointer => {
- if (!operand_ty.isSlice(zcu)) {
- break :item_ty operand_ty;
- }
- },
+ .@"union" => {
+ const enum_ty = operand_ty.unionTagType(zcu) orelse {
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{});
+ errdefer msg.destroy(sema.gpa);
+ if (operand_ty.srcLocOrNull(zcu)) |union_src| {
+ try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
+ }
+ break :msg msg;
+ });
+ };
+ break :item_ty enum_ty;
+ },
- else => {},
- }
- return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
- };
+ .pointer => {
+ if (!operand_ty.isSlice(zcu)) {
+ break :item_ty operand_ty;
+ }
+ },
- if (zir_switch.has_continue and !block.isComptime()) {
- if (try operand_ty.comptimeOnlySema(pt)) {
- // Even if the operand is comptime-known, this `switch` is runtime.
- return sema.failWithOwnedErrorMsg(block, msg: {
- const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
- errdefer msg.destroy(gpa);
- try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
- try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty);
- break :msg msg;
- });
- }
- try sema.validateRuntimeValue(block, operand_src, raw_operand);
+ else => {},
}
-
- break :check_operand .{ operand_ty, item_ty };
+ return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
};
+ if (zir_switch.has_continue and !block.isComptime()) {
+ if (operand_ty.comptimeOnly(zcu)) {
+ // Even if the operand is comptime-known, this `switch` is runtime.
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
+ try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty);
+ break :msg msg;
+ });
+ }
+ try sema.validateRuntimeValue(block, operand_src, raw_operand);
+ }
+
const has_else = zir_switch.else_case != null;
const has_under = zir_switch.has_under;
@@ -12305,7 +11648,7 @@ fn resolveSwitchBlock(
child_block: *Block,
operand: SwitchOperand,
raw_operand_ty: Type,
- maybe_lazy_cond_val: Value,
+ cond_val: Value,
merges: *Block.Merges,
switch_inst: Zir.Inst.Index,
zir_switch: *const Zir.UnwrappedSwitchBlock,
@@ -12325,9 +11668,6 @@ fn resolveSwitchBlock(
const err_set = item_ty.zigTypeTag(zcu) == .error_set;
const cond_ref = operand.simple.cond;
- // We have to resolve lazy values to ensure that comparisons with switch
- // prong items don't produce false negatives.
- const cond_val = try sema.resolveLazyValue(maybe_lazy_cond_val);
const case_vals = validated_switch.case_vals;
var case_val_idx: usize = 0;
@@ -12617,14 +11957,12 @@ fn wantSwitchProngBodyAnalysis(
) bool {
const zcu = sema.pt.zcu;
if (union_originally) {
- const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
- const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
+ const item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
const field_ty = operand_ty.unionFieldType(item_val, zcu).?;
if (field_ty.isNoReturn(zcu)) return false;
}
if (err_set and prong_is_comptime_unreach) {
- const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
- const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
+ const item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
const err_name = item_val.getErrorName(zcu).unwrap().?;
if (!Type.errorSetHasFieldIp(&zcu.intern_pool, operand_ty.toIntern(), err_name)) return false;
}
@@ -12807,7 +12145,7 @@ fn analyzeSwitchPayloadCapture(
const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
if (capture_by_ref) {
const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu);
- const ptr_field_ty = try pt.ptrTypeSema(.{
+ const ptr_field_ty = try pt.ptrType(.{
.child = field_ty.toIntern(),
.flags = .{
.is_const = operand_ptr_info.flags.is_const,
@@ -12821,6 +12159,7 @@ fn analyzeSwitchPayloadCapture(
const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
return .fromIntern(tag_and_val.val);
}
+ if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
return case_block.addStructFieldVal(operand_val, field_index, field_ty);
}
} else if (capture_by_ref) {
@@ -12914,13 +12253,27 @@ fn analyzeSwitchPayloadCapture(
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.ptrTypeSema(.{
+ 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,
- .alignment = union_obj.fieldAlign(ip, field_idx),
+ // 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);
+ },
},
});
dummy.* = try pt.undefRef(field_ptr_ty);
@@ -12963,6 +12316,8 @@ fn analyzeSwitchPayloadCapture(
return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
}
+ if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
+
if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| {
if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
@@ -13119,7 +12474,7 @@ fn analyzeSwitchPayloadCapture(
try sema.air_instructions.append(sema.gpa, .{
.tag = .get_union_tag,
.data = .{ .ty_op = .{
- .ty = .fromIntern(union_obj.enum_tag_ty),
+ .ty = .fromIntern(union_obj.enum_tag_type),
.operand = operand_val,
} },
});
@@ -13261,17 +12616,8 @@ fn resolveSwitchItem(
}
break :item_ref try sema.coerce(block, item_ty, uncoerced, item_src);
};
- const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item });
-
- // We have to resolve lazy values here to avoid false negatives when detecting
- // duplicate items and comparing items to a comptime-known switch operand.
-
- const val = try sema.resolveLazyValue(maybe_lazy);
- const ref: Air.Inst.Ref = if (val.toIntern() == maybe_lazy.toIntern())
- item_ref
- else
- .fromValue(val);
- return .{ .{ .ref = ref, .val = val }, end };
+ const val = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item });
+ return .{ .{ .ref = item_ref, .val = val }, end };
}
fn validateSwitchItemOrRange(
@@ -13488,7 +12834,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
const ty = try sema.resolveType(block, ty_src, extra.lhs);
const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name });
- try ty.resolveFields(pt);
+ try sema.ensureLayoutResolved(ty);
const ip = &zcu.intern_pool;
const has_field = hf: {
@@ -13510,7 +12856,8 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
},
.union_type => {
const union_type = ip.loadUnionType(ty.toIntern());
- break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null;
+ const enum_type = ip.loadEnumType(union_type.enum_tag_type);
+ break :hf enum_type.nameIndex(ip, field_name) != null;
},
.enum_type => {
break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null;
@@ -13569,10 +12916,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
switch (file.getMode()) {
.zig => {
try pt.ensureFileAnalyzed(file_index);
- const ty = zcu.fileRootType(file_index);
- try sema.declareDependency(.{ .interned = ty });
+ const ty: Type = .fromInterned(zcu.fileRootType(file_index));
try sema.addTypeReferenceEntry(operand_src, ty);
- return Air.internedToRef(ty);
+ return .fromType(ty);
},
.zon => {
const res_ty: InternPool.Index = b: {
@@ -13692,8 +13038,8 @@ fn zirShl(
// we already know `scalar_rhs_ty` is valid for `.shl` -- we only need to validate for `.shl_sat`.
if (air_tag == .shl_sat) _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs);
+ const maybe_lhs_val = try sema.resolveValue(lhs);
+ const maybe_rhs_val = try sema.resolveValue(rhs);
const runtime_src = rs: {
if (maybe_rhs_val) |rhs_val| {
@@ -13713,11 +13059,11 @@ fn zirShl(
const bits = scalar_ty.intInfo(zcu).bits;
switch (rhs_ty.zigTypeTag(zcu)) {
.int, .comptime_int => {
- switch (try rhs_val.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {
if (air_tag != .shl_sat) {
var rhs_space: Value.BigIntSpace = undefined;
- const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt);
+ const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu);
if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
}
@@ -13736,11 +13082,11 @@ fn zirShl(
.shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, elem_idx),
else => unreachable,
};
- switch (try rhs_elem.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) {
.gt => {
if (air_tag != .shl_sat) {
var rhs_elem_space: Value.BigIntSpace = undefined;
- const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt);
+ const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu);
if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
}
@@ -13769,7 +13115,7 @@ fn zirShl(
.shl, .shl_exact => try sema.checkAllScalarsDefined(block, lhs_src, lhs_val),
else => unreachable,
}
- if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs;
+ if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs;
}
}
break :rs rhs_src;
@@ -13785,13 +13131,13 @@ fn zirShl(
const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count);
if (!rhs_ty.isVector(zcu)) break :rt_rhs try pt.intValue(
rt_rhs_scalar_ty,
- @min(try rhs_val.getUnsignedIntSema(pt) orelse bit_count, bit_count),
+ @min(rhs_val.getUnsignedInt(zcu) orelse bit_count, bit_count),
);
const rhs_len = rhs_ty.vectorLen(zcu);
const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len);
for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue(
rt_rhs_scalar_ty,
- @min(try (try rhs_val.elemValue(pt, i)).getUnsignedIntSema(pt) orelse bit_count, bit_count),
+ @min((try rhs_val.elemValue(pt, i)).getUnsignedInt(zcu) orelse bit_count, bit_count),
)).toIntern();
break :rt_rhs try pt.aggregateValue(try pt.vectorType(.{
.len = rhs_len,
@@ -13875,8 +13221,8 @@ fn zirShr(
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
const scalar_ty = lhs_ty.scalarType(zcu);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs);
+ const maybe_lhs_val = try sema.resolveValue(lhs);
+ const maybe_rhs_val = try sema.resolveValue(rhs);
const runtime_src = rs: {
if (maybe_rhs_val) |rhs_val| {
@@ -13893,10 +13239,10 @@ fn zirShr(
const bits = scalar_ty.intInfo(zcu).bits;
switch (rhs_ty.zigTypeTag(zcu)) {
.int, .comptime_int => {
- switch (try rhs_val.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {
var rhs_space: Value.BigIntSpace = undefined;
- const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt);
+ const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu);
if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
}
@@ -13912,10 +13258,10 @@ fn zirShr(
if (rhs_elem.isUndef(zcu)) {
return sema.failWithUseOfUndef(block, rhs_src, elem_idx);
}
- switch (try rhs_elem.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) {
.gt => {
var rhs_elem_space: Value.BigIntSpace = undefined;
- const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt);
+ const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu);
if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
}
@@ -13936,7 +13282,7 @@ fn zirShr(
}
if (maybe_lhs_val) |lhs_val| {
try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
- if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs;
+ if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs;
}
}
break :rs rhs_src;
@@ -14011,8 +13357,8 @@ fn zirBitwise(
const runtime_src = runtime: {
// TODO: ask the linker what kind of relocations are available, and
// in some cases emit a Value that means "this decl's address AND'd with this operand".
- if (try sema.resolveValueResolveLazy(casted_lhs)) |lhs_val| {
- if (try sema.resolveValueResolveLazy(casted_rhs)) |rhs_val| {
+ if (try sema.resolveValue(casted_lhs)) |lhs_val| {
+ if (try sema.resolveValue(casted_rhs)) |rhs_val| {
const result_val = switch (air_tag) {
// zig fmt: off
.bit_and => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"and"),
@@ -14106,13 +13452,13 @@ fn analyzeTupleCat(
var i: u32 = 0;
while (i < lhs_len) : (i += 1) {
types[i] = lhs_ty.fieldType(i, zcu).toIntern();
- const default_val = lhs_ty.structFieldDefaultValue(i, zcu);
- values[i] = default_val.toIntern();
const operand_src = block.src(.{ .array_cat_lhs = .{
.array_cat_offset = src_node,
.elem_index = i,
} });
- if (default_val.toIntern() == .unreachable_value) {
+ if (lhs_ty.structFieldDefaultValue(i, zcu)) |default_val| {
+ values[i] = default_val.toIntern();
+ } else {
runtime_src = operand_src;
values[i] = .none;
}
@@ -14120,13 +13466,13 @@ fn analyzeTupleCat(
i = 0;
while (i < rhs_len) : (i += 1) {
types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern();
- const default_val = rhs_ty.structFieldDefaultValue(i, zcu);
- values[i + lhs_len] = default_val.toIntern();
const operand_src = block.src(.{ .array_cat_rhs = .{
.array_cat_offset = src_node,
.elem_index = i,
} });
- if (default_val.toIntern() == .unreachable_value) {
+ if (rhs_ty.structFieldDefaultValue(i, zcu)) |default_val| {
+ values[i + lhs_len] = default_val.toIntern();
+ } else {
runtime_src = operand_src;
values[i + lhs_len] = .none;
}
@@ -14290,8 +13636,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
var elem_i: u32 = 0;
while (elem_i < lhs_len) : (elem_i += 1) {
const lhs_elem_i = elem_i;
- const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else Value.@"unreachable";
- const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(pt, lhs_elem_i) else elem_default_val;
+ const elem_default_val: ?Value = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else null;
+ const elem_val = elem_default_val orelse try lhs_sub_val.elemValue(pt, lhs_elem_i);
const elem_val_inst = Air.internedToRef(elem_val.toIntern());
const operand_src = block.src(.{ .array_cat_lhs = .{
.array_cat_offset = inst_data.src_node,
@@ -14303,8 +13649,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
}
while (elem_i < result_len) : (elem_i += 1) {
const rhs_elem_i = elem_i - lhs_len;
- const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else Value.@"unreachable";
- const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(pt, rhs_elem_i) else elem_default_val;
+ const elem_default_val: ?Value = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else null;
+ const elem_val = elem_default_val orelse try rhs_sub_val.elemValue(pt, rhs_elem_i);
const elem_val_inst = Air.internedToRef(elem_val.toIntern());
const operand_src = block.src(.{ .array_cat_rhs = .{
.array_cat_offset = inst_data.src_node,
@@ -14324,18 +13670,18 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
try sema.requireRuntimeBlock(block, src, runtime_src);
if (ptr_addrspace) |ptr_as| {
- const constant_alloc_ty = try pt.ptrTypeSema(.{
+ const constant_alloc_ty = try pt.ptrType(.{
.child = result_ty.toIntern(),
.flags = .{
.address_space = ptr_as,
.is_const = true,
},
});
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = result_ty.toIntern(),
.flags = .{ .address_space = ptr_as },
});
- const elem_ptr_ty = try pt.ptrTypeSema(.{
+ const elem_ptr_ty = try pt.ptrType(.{
.child = resolved_elem_ty.toIntern(),
.flags = .{ .address_space = ptr_as },
});
@@ -14347,7 +13693,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
if (lhs_ty.zigTypeTag(zcu) == .pointer and
rhs_ty.zigTypeTag(zcu) == .pointer)
{
- const slice_ty = try pt.ptrTypeSema(.{
+ const slice_ty = try pt.ptrType(.{
.child = resolved_elem_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -14486,7 +13832,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins
.none => null,
else => Value.fromInterned(ptr_info.sentinel),
},
- .len = try val.sliceLen(pt),
+ .len = val.sliceLen(zcu),
};
},
.one => {
@@ -14500,8 +13846,20 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins
.@"struct" => {
if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) {
assert(!peer_ty.isTuple(zcu));
+ const peer_elem_ty = switch (peer_ty.zigTypeTag(zcu)) {
+ .pointer => switch (peer_ty.ptrSize(zcu)) {
+ .one => switch (peer_ty.childType(zcu).zigTypeTag(zcu)) {
+ .array, .vector => peer_ty.childType(zcu).childType(zcu),
+ .@"struct" => return null,
+ else => unreachable,
+ },
+ .many, .c, .slice => peer_ty.childType(zcu),
+ },
+ .vector, .array => peer_ty.childType(zcu),
+ else => unreachable,
+ };
return .{
- .elem_type = peer_ty.elemType2(zcu),
+ .elem_type = peer_elem_ty,
.sentinel = null,
.len = operand_ty.arrayLen(zcu),
};
@@ -14543,12 +13901,13 @@ fn analyzeTupleMul(
var runtime_src: ?LazySrcLoc = null;
for (0..tuple_len) |i| {
types[i] = operand_ty.fieldType(i, zcu).toIntern();
- values[i] = operand_ty.structFieldDefaultValue(i, zcu).toIntern();
const operand_src = block.src(.{ .array_cat_lhs = .{
.array_cat_offset = src_node,
.elem_index = @intCast(i),
} });
- if (values[i] == .unreachable_value) {
+ if (operand_ty.structFieldDefaultValue(i, zcu)) |default_val| {
+ values[i] = default_val.toIntern();
+ } else {
runtime_src = operand_src;
values[i] = .none; // TODO don't treat unreachable_value as special
}
@@ -14714,7 +14073,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
}
if (ptr_addrspace) |ptr_as| {
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = result_ty.toIntern(),
.flags = .{
.address_space = ptr_as,
@@ -14722,7 +14081,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
},
});
const alloc = try block.addTy(.alloc, alloc_ty);
- const elem_ptr_ty = try pt.ptrTypeSema(.{
+ const elem_ptr_ty = try pt.ptrType(.{
.child = lhs_info.elem_type.toIntern(),
.flags = .{ .address_space = ptr_as },
});
@@ -14859,8 +14218,8 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or
(lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float))
@@ -14968,8 +14327,8 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
// Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior.
@@ -15064,8 +14423,8 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
const allow_div_zero = !is_int and
resolved_type.toIntern() != .comptime_float_type and
@@ -15129,8 +14488,8 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
const allow_div_zero = !is_int and
resolved_type.toIntern() != .comptime_float_type and
@@ -15341,8 +14700,8 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
const lhs_maybe_negative = a: {
if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
@@ -15440,8 +14799,8 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
const allow_div_zero = !is_int and
resolved_type.toIntern() != .comptime_float_type and
@@ -15504,8 +14863,8 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
const allow_div_zero = !is_int and
resolved_type.toIntern() != .comptime_float_type and
@@ -15601,12 +14960,12 @@ fn zirOverflowArithmetic(
// to the result, even if it is undefined..
// Otherwise, if either of the argument is undefined, undefined is returned.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) {
+ if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
}
}
if (maybe_rhs_val) |rhs_val| {
- if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) {
+ if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
}
}
@@ -15627,7 +14986,7 @@ fn zirOverflowArithmetic(
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef(zcu)) {
break :result .{ .overflow_bit = .undef, .wrapped = .undef };
- } else if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
+ } else if (rhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
} else if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef(zcu)) {
@@ -15642,12 +15001,12 @@ fn zirOverflowArithmetic(
.mul_with_overflow => {
// If either of the arguments is zero, the result is zero and no overflow occured.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef(zcu) and try lhs_val.compareAllWithZeroSema(.eq, pt)) {
+ if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
}
}
if (maybe_rhs_val) |rhs_val| {
- if (!rhs_val.isUndef(zcu) and try rhs_val.compareAllWithZeroSema(.eq, pt)) {
+ if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
}
}
@@ -15694,10 +15053,10 @@ fn zirOverflowArithmetic(
const bits = scalar_ty.intInfo(zcu).bits;
switch (rhs_ty.zigTypeTag(zcu)) {
.int, .comptime_int => {
- switch (try rhs_val.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {
var rhs_space: Value.BigIntSpace = undefined;
- const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt);
+ const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu);
if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
}
@@ -15711,10 +15070,10 @@ fn zirOverflowArithmetic(
for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
if (rhs_elem.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, elem_idx);
- switch (try rhs_elem.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) {
.gt => {
var rhs_elem_space: Value.BigIntSpace = undefined;
- const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt);
+ const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu);
if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
}
@@ -15728,7 +15087,7 @@ fn zirOverflowArithmetic(
},
else => unreachable,
}
- if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
+ if (rhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
}
} else {
@@ -15737,7 +15096,7 @@ fn zirOverflowArithmetic(
}
if (maybe_lhs_val) |lhs_val| {
try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
- if (try lhs_val.compareAllWithZeroSema(.eq, pt)) {
+ if (lhs_val.compareAllWithZero(.eq, zcu)) {
break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
}
}
@@ -15817,16 +15176,16 @@ fn analyzeArithmetic(
if (zir_tag != .sub) {
return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction");
}
- if (!lhs_ty.elemType2(zcu).eql(rhs_ty.elemType2(zcu), zcu)) {
+ if (!lhs_ty.childType(zcu).eql(rhs_ty.childType(zcu), zcu)) {
return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{
lhs_ty.fmt(pt), rhs_ty.fmt(pt),
});
}
- const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu);
+ const elem_size = lhs_ty.childType(zcu).abiSize(zcu);
if (elem_size == 0) {
- return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{
- lhs_ty.elemType2(zcu).fmt(pt),
+ return sema.fail(block, src, "pointer subtraction requires element type '{f}' to have runtime bits", .{
+ lhs_ty.childType(zcu).fmt(pt),
});
}
@@ -15875,11 +15234,7 @@ fn analyzeArithmetic(
else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
};
- if (!try lhs_ty.elemType2(zcu).hasRuntimeBitsSema(pt)) {
- return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{
- lhs_ty.elemType2(zcu).fmt(pt),
- });
- }
+ try sema.ensureLayoutResolved(lhs_ty.childType(zcu));
return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
},
}
@@ -15915,8 +15270,8 @@ fn analyzeArithmetic(
else => unreachable,
};
- const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
- const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
+ const maybe_lhs_val = try sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = try sema.resolveValue(casted_rhs);
if (maybe_lhs_val) |lhs_val| {
if (maybe_rhs_val) |rhs_val| {
@@ -15972,6 +15327,7 @@ fn analyzeArithmetic(
return block.addBinOp(air_tag, casted_lhs, casted_rhs);
}
+/// Asserts that the layout of the pointer child type is already resolved.
fn analyzePtrArithmetic(
sema: *Sema,
block: *Block,
@@ -15993,7 +15349,10 @@ fn analyzePtrArithmetic(
const ptr_info = ptr_ty.ptrInfo(zcu);
assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c);
- if ((try sema.typeHasOnePossibleValue(.fromInterned(ptr_info.child))) != null) {
+ const elem_ty: Type = .fromInterned(ptr_info.child);
+ elem_ty.assertHasLayout(zcu);
+
+ if (elem_ty.abiSize(zcu) == 0) {
// Offset will be multiplied by zero, so result is the same as the base pointer.
return ptr;
}
@@ -16007,9 +15366,9 @@ fn analyzePtrArithmetic(
}
// 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 = try Type.fromInterned(ptr_info.child).abiSizeSema(pt);
+ 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, try off_val.toUnsignedIntSema(pt));
+ const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(zcu));
break :a elem_size * off_int;
} else elem_size;
@@ -16022,7 +15381,7 @@ fn analyzePtrArithmetic(
));
assert(new_align != .none);
- break :t try pt.ptrTypeSema(.{
+ break :t try pt.ptrType(.{
.child = ptr_info.child,
.sentinel = ptr_info.sentinel,
.flags = .{
@@ -16041,10 +15400,10 @@ fn analyzePtrArithmetic(
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, try offset_val.toUnsignedIntSema(pt));
+ 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 = try Type.fromInterned(ptr_info.child).abiSizeSema(pt);
+ 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 {
@@ -16248,6 +15607,7 @@ fn zirAsm(
buffer[input.c.len + 1 + input.n.len] = 0;
sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
}
+ if (try expr_ty.toType().onePossibleValue(pt)) |opv| return .fromValue(opv);
return asm_air;
}
@@ -16343,7 +15703,6 @@ fn analyzeCmpUnionTag(
const pt = sema.pt;
const zcu = pt.zcu;
const union_ty = sema.typeOf(un);
- try union_ty.resolveFields(pt);
const union_tag_ty = union_ty.unionTagType(zcu) orelse {
const msg = msg: {
const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
@@ -16534,10 +15893,11 @@ fn runtimeBoolCmp(
fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
+ const zcu = pt.zcu;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
const ty = try sema.resolveType(block, operand_src, inst_data.operand);
- switch (ty.zigTypeTag(pt.zcu)) {
+ switch (ty.zigTypeTag(zcu)) {
.@"fn",
.noreturn,
.undefined,
@@ -16568,8 +15928,8 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
.@"anyframe",
=> {},
}
- const val = try ty.abiSizeLazy(pt);
- return Air.internedToRef(val.toIntern());
+ try sema.ensureLayoutResolved(ty);
+ return .fromValue(try pt.intValue(.comptime_int, ty.abiSize(zcu)));
}
fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -16609,8 +15969,8 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
.@"anyframe",
=> {},
}
- const bit_size = try operand_ty.bitSizeSema(pt);
- return pt.intRef(.comptime_int, bit_size);
+ try sema.ensureLayoutResolved(operand_ty);
+ return .fromValue(try pt.intValue(.comptime_int, operand_ty.bitSize(zcu)));
}
fn zirThis(
@@ -16619,34 +15979,16 @@ fn zirThis(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
_ = extended;
- const pt = sema.pt;
- const zcu = pt.zcu;
- const namespace = pt.zcu.namespacePtr(block.namespace);
+ const zcu = sema.pt.zcu;
+ const namespace = zcu.namespacePtr(block.namespace);
- switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) {
- .opaque_type => {
- // Opaque types are never outdated since they don't undergo type resolution, so nothing to do!
- return Air.internedToRef(namespace.owner_type);
- },
- .struct_type, .union_type => {
- const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
- try sema.declareDependency(.{ .interned = new_ty });
- return Air.internedToRef(new_ty);
- },
- .enum_type => {
- const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
- try sema.declareDependency(.{ .interned = new_ty });
- // Since this is an enum, it has to be resolved immediately.
- // `ensureTypeUpToDate` has resolved the new type if necessary.
- // We just need to check for resolution failures.
- const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
- if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
- return error.AnalysisFail;
- }
- return Air.internedToRef(new_ty);
- },
+ switch (zcu.intern_pool.indexToKey(namespace.owner_type)) {
+ .opaque_type, .struct_type, .union_type => {},
+ // Enum inits are resolved eagerly. TODO MLUGG: honestly i don't think they SHOULD be lol
+ .enum_type => try sema.ensureFieldInitsResolved(.fromInterned(namespace.owner_type)),
else => unreachable,
}
+ return .fromIntern(namespace.owner_type);
}
fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
@@ -16739,7 +16081,7 @@ fn zirRetAddr(
_ = sema;
_ = extended;
if (block.isComptime()) {
- // TODO: we could give a meaningful lazy value here. #14938
+ // TODO: we could give a meaningful value here. #14938
return .zero_usize;
} else {
return block.addNoOp(.ret_addr);
@@ -16886,6 +16228,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
try sema.declareDependency(.{ .namespace = type_decl_inst });
}
+ try sema.ensureLayoutResolved(ty);
+
switch (ty.zigTypeTag(zcu)) {
.type,
.void,
@@ -16934,7 +16278,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.child = param_info_ty.toIntern(),
});
const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_vals)).toIntern();
- const slice_ty = (try pt.ptrTypeSema(.{
+ const slice_ty = (try pt.ptrType(.{
.child = param_info_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -16976,11 +16320,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
error.OutOfMemory => |e| return e,
};
+ // MLUGG TODO
+ const func_is_generic = false;
+
const field_values: [5]InternPool.Index = .{
// calling_convention: CallingConvention,
callconv_val.toIntern(),
// is_generic: bool,
- Value.makeBool(func_ty_info.is_generic).toIntern(),
+ Value.makeBool(func_is_generic).toIntern(),
// is_var_args: bool,
Value.makeBool(func_ty_info.is_var_args).toIntern(),
// return_type: ?type,
@@ -17015,7 +16362,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const field_vals = .{
// bits: u16,
- (try pt.intValue(.u16, ty.bitSize(zcu))).toIntern(),
+ (try pt.intValue(.u16, ty.floatBits(zcu.getTarget()))).toIntern(),
};
return Air.internedToRef((try pt.internUnion(.{
.ty = type_info_ty.toIntern(),
@@ -17025,10 +16372,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
},
.pointer => {
const info = ty.ptrInfo(zcu);
- const alignment = if (info.flags.alignment.toByteUnits()) |alignment|
- try pt.intValue(.comptime_int, alignment)
- else
- try Type.fromInterned(info.child).lazyAbiAlignment(pt);
+ const alignment_val = try pt.intValue(.comptime_int, bytes: {
+ if (info.flags.alignment.toByteUnits()) |b| break :bytes b;
+ const elem_ty: Type = .fromInterned(info.child);
+ // MLUGG TODO: this resolution is sus, but i doubt i'll solve it in this branch
+ try sema.ensureLayoutResolved(elem_ty);
+ break :bytes elem_ty.abiAlignment(zcu).toByteUnits().?;
+ });
const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace);
const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer");
@@ -17042,7 +16392,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
// is_volatile: bool,
Value.makeBool(info.flags.is_volatile).toIntern(),
// alignment: comptime_int,
- alignment.toIntern(),
+ alignment_val.toIntern(),
// address_space: AddressSpace
(try pt.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).toIntern(),
// child: type,
@@ -17159,7 +16509,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
};
// Build our ?[]const Error value
- const slice_errors_ty = try pt.ptrTypeSema(.{
+ const slice_errors_ty = try pt.ptrType(.{
.child = error_field_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -17215,19 +16565,19 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
})));
},
.@"enum" => {
- const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive);
+ const enum_obj = ip.loadEnumType(ty.toIntern());
+ const is_exhaustive: Value = .makeBool(!enum_obj.nonexhaustive);
const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField");
- const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len);
+ const enum_field_vals = try sema.arena.alloc(InternPool.Index, enum_obj.field_names.len);
for (enum_field_vals, 0..) |*field_val, tag_index| {
- const enum_type = ip.loadEnumType(ty.toIntern());
- const value_val = if (enum_type.values.len > 0)
+ const value_val = if (enum_obj.field_values.len > 0)
try ip.getCoercedInts(
gpa,
io,
pt.tid,
- ip.indexToKey(enum_type.values.get(ip)[tag_index]).int,
+ ip.indexToKey(enum_obj.field_values.get(ip)[tag_index]).int,
.comptime_int_type,
)
else
@@ -17235,7 +16585,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
// TODO: write something like getCoercedInts to avoid needing to dupe
const name_val = v: {
- const tag_name = enum_type.names.get(ip)[tag_index];
+ const tag_name = enum_obj.field_names.get(ip)[tag_index];
const tag_name_len = tag_name.length(ip);
const new_decl_ty = try pt.arrayType(.{
.len = tag_name_len,
@@ -17275,7 +16625,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.child = enum_field_ty.toIntern(),
});
const new_decl_val = (try pt.aggregateValue(fields_array_ty, enum_field_vals)).toIntern();
- const slice_ty = (try pt.ptrTypeSema(.{
+ const slice_ty = (try pt.ptrType(.{
.child = enum_field_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -17303,7 +16653,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const field_values = .{
// tag_type: type,
- ip.loadEnumType(ty.toIntern()).tag_ty,
+ ip.loadEnumType(ty.toIntern()).int_tag_type,
// fields: []const EnumField,
fields_val,
// decls: []const Declaration,
@@ -17321,17 +16671,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union");
const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField");
- try ty.resolveLayout(pt); // Getting alignment requires type layout
- const union_obj = zcu.typeToUnion(ty).?;
- const tag_type = union_obj.loadTagType(ip);
- const layout = union_obj.flagsUnordered(ip).layout;
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
+ const layout = union_obj.layout;
- const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len);
+ const union_field_vals = try gpa.alloc(InternPool.Index, enum_obj.field_names.len);
defer gpa.free(union_field_vals);
for (union_field_vals, 0..) |*field_val, field_index| {
const name_val = v: {
- const field_name = tag_type.names.get(ip)[field_index];
+ const field_name = enum_obj.field_names.get(ip)[field_index];
const field_name_len = field_name.length(ip);
const new_decl_ty = try pt.arrayType(.{
.len = field_name_len,
@@ -17357,7 +16706,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
};
const alignment = switch (layout) {
- .auto, .@"extern" => try ty.fieldAlignmentSema(field_index, pt),
+ .auto, .@"extern" => ty.resolvedFieldAlignment(field_index, zcu),
.@"packed" => .none,
};
@@ -17379,7 +16728,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.child = union_field_ty.toIntern(),
});
const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_vals)).toIntern();
- const slice_ty = (try pt.ptrTypeSema(.{
+ const slice_ty = (try pt.ptrType(.{
.child = union_field_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -17431,8 +16780,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct");
const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField");
- try ty.resolveLayout(pt); // Getting alignment requires type layout
-
var struct_field_vals: []InternPool.Index = &.{};
defer gpa.free(struct_field_vals);
fv: {
@@ -17468,8 +16815,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
} });
};
- try Type.fromInterned(field_ty).resolveLayout(pt);
-
const is_comptime = field_val != .none;
const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null;
const default_val_ptr = try sema.optRefValue(opt_default_val);
@@ -17492,16 +16837,17 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.struct_type => ip.loadStructType(ty.toIntern()),
else => unreachable,
};
+ try sema.ensureFieldInitsResolved(ty); // can't do this sooner, since it's not allowed on tuples
struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
- try ty.resolveStructFieldInits(pt);
-
for (struct_field_vals, 0..) |*field_val, field_index| {
- const field_name = struct_type.fieldName(ip, field_index);
+ const field_name = struct_type.field_names.get(ip)[field_index];
const field_name_len = field_name.length(ip);
const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
- const field_init = struct_type.fieldInit(ip, field_index);
- const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
+ const field_default: InternPool.Index = if (struct_type.field_defaults.len > 0) d: {
+ break :d struct_type.field_defaults.get(ip)[field_index];
+ } else .none;
+ const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index);
const name_val = v: {
const new_decl_ty = try pt.arrayType(.{
.len = field_name_len,
@@ -17526,15 +16872,11 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
} });
};
- const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init);
+ 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),
.@"packed" => .none,
- else => try field_ty.structFieldAlignmentSema(
- struct_type.fieldAlign(ip, field_index),
- struct_type.layout,
- pt,
- ),
};
const struct_field_fields = .{
@@ -17559,7 +16901,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.child = struct_field_ty.toIntern(),
});
const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_vals)).toIntern();
- const slice_ty = (try pt.ptrTypeSema(.{
+ const slice_ty = (try pt.ptrType(.{
.child = struct_field_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -17585,9 +16927,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const backing_integer_val = try pt.intern(.{ .opt = .{
.ty = (try pt.optionalType(.type_type)).toIntern(),
- .val = if (zcu.typeToPackedStruct(ty)) |packed_struct| val: {
- assert(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)).isInt(zcu));
- break :val packed_struct.backingIntTypeUnordered(ip);
+ .val = if (zcu.typeToPackedStruct(ty)) |struct_obj| val: {
+ assert(Type.fromInterned(struct_obj.packed_backing_int_type).isInt(zcu));
+ break :val struct_obj.packed_backing_int_type;
} else .none,
} });
@@ -17616,7 +16958,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.@"opaque" => {
const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque");
- try ty.resolveFields(pt);
const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
const field_values = .{
@@ -17658,7 +16999,7 @@ fn typeInfoDecls(
.child = declaration_ty.toIntern(),
});
const new_decl_val = (try pt.aggregateValue(array_decl_ty, decl_vals.items)).toIntern();
- const slice_ty = (try pt.ptrTypeSema(.{
+ const slice_ty = (try pt.ptrType(.{
.child = declaration_ty.toIntern(),
.flags = .{
.size = .slice,
@@ -17783,22 +17124,12 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi
const zcu = pt.zcu;
switch (operand.zigTypeTag(zcu)) {
.comptime_int => return .comptime_int,
- .int => {
- const bits = operand.bitSize(zcu);
- const count = if (bits == 0)
- 0
- else blk: {
- var count: u16 = 0;
- var s = bits - 1;
- while (s != 0) : (s >>= 1) {
- count += 1;
- }
- break :blk count;
- };
- return pt.intType(.unsigned, count);
- },
+ .int => return pt.intType(.unsigned, switch (operand.intInfo(zcu).bits) {
+ 0 => 0,
+ else => |b| std.math.log2_int_ceil(u16, b),
+ }),
.vector => {
- const elem_ty = operand.elemType2(zcu);
+ const elem_ty = operand.childType(zcu);
const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
return pt.vectorType(.{
.len = operand.vectorLen(zcu),
@@ -18082,13 +17413,15 @@ fn zirIsNonNullPtr(
const src = block.nodeOffset(inst_data.src_node);
const ptr = try sema.resolveInst(inst_data.operand);
const ptr_ty = sema.typeOf(ptr);
- try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu));
+ assert(ptr_ty.zigTypeTag(zcu) == .pointer);
+ const nullable_ty = ptr_ty.childType(zcu);
+ try sema.checkNullableType(block, src, nullable_ty);
if (try sema.resolveValue(ptr)) |ptr_val| {
- if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| {
- return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true);
+ if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |nullable_val| {
+ return sema.analyzeIsNull(block, .fromValue(nullable_val), true);
}
}
- if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| {
+ if (nullable_ty.isNullFromType(zcu)) |is_null| {
return if (is_null) .bool_false else .bool_true;
}
return block.addUnOp(.is_non_null_ptr, ptr);
@@ -18125,7 +17458,10 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(inst_data.src_node);
const ptr = try sema.resolveInst(inst_data.operand);
- try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(zcu));
+ const ptr_ty = sema.typeOf(ptr);
+ assert(ptr_ty.zigTypeTag(zcu) == .pointer);
+ const error_ty = ptr_ty.childType(zcu);
+ try sema.checkErrorType(block, src, error_ty);
const loaded = try sema.analyzeLoad(block, src, ptr, src);
return sema.analyzeIsNonErr(block, src, loaded);
}
@@ -18294,6 +17630,11 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
} },
});
sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
+
+ // The payload type might still be OPV, in which case `try_inst` is just there for the runtime
+ // control flow and we should return a comptime-known result.
+ if (try err_union_ty.errorUnionPayload(zcu).onePossibleValue(pt)) |opv| return .fromValue(opv);
+
return try_inst;
}
@@ -18347,7 +17688,7 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
const operand_ty = sema.typeOf(operand);
const ptr_info = operand_ty.ptrInfo(zcu);
- const res_ty = try pt.ptrTypeSema(.{
+ const res_ty = try pt.ptrType(.{
.child = err_union_ty.errorUnionPayload(zcu).toIntern(),
.flags = .{
.is_const = ptr_info.flags.is_const,
@@ -18512,7 +17853,7 @@ fn zirRetImplicit(
const operand = try sema.resolveInst(inst_data.operand);
const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero });
- const base_tag = sema.fn_ret_ty.baseZigTypeTag(zcu);
+ const base_tag = sema.fn_ret_ty.optEuBaseType(zcu).zigTypeTag(zcu);
if (base_tag == .noreturn) {
const msg = msg: {
const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{
@@ -18809,8 +18150,6 @@ fn analyzeRet(
return sema.failWithOwnedErrorMsg(block, msg);
}
- try sema.fn_ret_ty.resolveLayout(pt);
-
try sema.validateRuntimeValue(block, operand_src, operand);
const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret;
@@ -18889,16 +18228,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
extra_i += 1;
const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src);
const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" });
- // Check if this happens to be the lazy alignment of our element type, in
- // which case we can make this 0 without resolving it.
- switch (zcu.intern_pool.indexToKey(val.toIntern())) {
- .int => |int| switch (int.storage) {
- .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none,
- else => {},
- },
- else => {},
- }
- const align_bytes = (try val.getUnsignedIntSema(pt)).?;
+ const align_bytes = val.toUnsignedInt(zcu);
break :blk try sema.validateAlign(block, align_src, align_bytes);
} else .none;
@@ -18928,7 +18258,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
});
}
- const elem_bit_size = try elem_ty.bitSizeSema(pt);
+ try sema.ensureLayoutResolved(elem_ty);
+ const elem_bit_size = elem_ty.bitSize(zcu);
if (elem_bit_size > host_size * 8 - bit_offset) {
return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{
elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
@@ -18957,16 +18288,16 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
}
}
- if (host_size != 0 and !try sema.validatePackedType(elem_ty)) {
+ if (host_size != 0 and !elem_ty.packable(zcu)) {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)});
errdefer msg.destroy(sema.gpa);
- try sema.explainWhyTypeIsNotPacked(msg, elem_ty_src, elem_ty);
+ try sema.explainWhyTypeIsNotPackable(msg, elem_ty_src, elem_ty);
break :msg msg;
});
}
- const ty = try pt.ptrTypeSema(.{
+ const ty = try pt.ptrType(.{
.child = elem_ty.toIntern(),
.sentinel = sentinel,
.flags = .{
@@ -18996,6 +18327,8 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
const pt = sema.pt;
const zcu = pt.zcu;
+ try sema.ensureLayoutResolved(obj_ty);
+
switch (obj_ty.zigTypeTag(zcu)) {
.@"struct" => return sema.structInitEmpty(block, obj_ty, src, src),
.array, .vector => return sema.arrayInitEmpty(block, src, obj_ty),
@@ -19058,6 +18391,9 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
.child = ptr_ty.childType(zcu).toIntern(),
});
} else ty_operand;
+
+ try sema.ensureLayoutResolved(init_ty);
+
const obj_ty = init_ty.optEuBaseType(zcu);
const empty_ref = switch (obj_ty.zigTypeTag(zcu)) {
@@ -19076,6 +18412,7 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
}
}
+/// Asserts that the layout of `struct_ty` is already resolved.
fn structInitEmpty(
sema: *Sema,
block: *Block,
@@ -19087,7 +18424,7 @@ fn structInitEmpty(
const zcu = pt.zcu;
const gpa = sema.gpa;
// This logic must be synchronized with that in `zirStructInit`.
- try struct_ty.resolveFields(pt);
+ struct_ty.assertHasLayout(zcu);
// The init values to use for the struct instance.
const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu));
@@ -19202,8 +18539,8 @@ fn zirStructInit(
// The type wasn't actually known, so treat this as an anon struct init.
return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref);
};
+ try sema.ensureLayoutResolved(result_ty);
const resolved_ty = result_ty.optEuBaseType(zcu);
- try resolved_ty.resolveLayout(pt);
if (resolved_ty.zigTypeTag(zcu) == .@"struct") {
// This logic must be synchronized with that in `zirStructInitEmpty`.
@@ -19226,7 +18563,6 @@ fn zirStructInit(
var field_i: u32 = 0;
var extra_index = extra.end;
- const is_packed = resolved_ty.containerLayout(zcu) == .@"packed";
while (field_i < extra.data.fields_len) : (field_i += 1) {
const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
extra_index = item.end;
@@ -19251,16 +18587,16 @@ fn zirStructInit(
const uncoerced_init = try sema.resolveInst(item.data.init);
const field_ty = resolved_ty.fieldType(field_index, zcu);
field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
- if (!is_packed) {
- try resolved_ty.resolveStructFieldInits(pt);
- if (try resolved_ty.structFieldValueComptime(pt, field_index)) |default_value| {
- const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
- return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
- };
-
- if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) {
- return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
- }
+ if (resolved_ty.structFieldIsComptime(field_index, zcu)) {
+ if (!resolved_ty.isTuple(zcu)) {
+ try sema.ensureFieldInitsResolved(resolved_ty);
+ }
+ const default_value = (try resolved_ty.structFieldValueComptime(pt, field_index)).?;
+ const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
+ return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
+ };
+ if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) {
+ return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
}
}
}
@@ -19315,7 +18651,7 @@ fn zirStructInit(
return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
}
- if (try resolved_ty.comptimeOnlySema(pt)) {
+ if (resolved_ty.comptimeOnly(zcu)) {
return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{
.ty = resolved_ty,
.msg = .union_init,
@@ -19326,7 +18662,7 @@ fn zirStructInit(
if (is_ref) {
const target = zcu.getTarget();
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = result_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -19334,9 +18670,8 @@ fn zirStructInit(
const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true);
try sema.storePtr(block, src, field_ptr, init_inst);
- if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) {
- const new_tag = Air.internedToRef(tag_val.toIntern());
- _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag);
+ if (try tag_ty.onePossibleValue(pt) == null) {
+ _ = try block.addBinOp(.set_union_tag, base_ptr, .fromValue(tag_val));
}
return sema.makePtrConst(block, alloc);
}
@@ -19409,20 +18744,24 @@ fn finishStructInit(
continue;
}
- try struct_ty.resolveStructFieldInits(pt);
+ try sema.ensureFieldInitsResolved(struct_ty);
- const field_init = struct_type.fieldInit(ip, i);
- if (field_init == .none) {
- const field_name = struct_type.field_names.get(ip)[i];
- const template = "missing struct field: {f}";
- const args = .{field_name.fmt(ip)};
- if (root_msg) |msg| {
- try sema.errNote(init_src, msg, template, args);
- } else {
- root_msg = try sema.errMsg(init_src, template, args);
- }
+ const field_default: InternPool.Index = d: {
+ if (struct_type.field_defaults.len == 0) break :d .none;
+ break :d struct_type.field_defaults.get(ip)[i];
+ };
+ if (field_default != .none) {
+ field_inits[i] = .fromIntern(field_default);
+ continue;
+ }
+
+ const field_name = struct_type.field_names.get(ip)[i];
+ const template = "missing struct field: {f}";
+ const args = .{field_name.fmt(ip)};
+ if (root_msg) |msg| {
+ try sema.errNote(init_src, msg, template, args);
} else {
- field_inits[i] = Air.internedToRef(field_init);
+ root_msg = try sema.errMsg(init_src, template, args);
}
}
},
@@ -19453,7 +18792,7 @@ fn finishStructInit(
return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
};
- if (try struct_ty.comptimeOnlySema(pt)) {
+ if (struct_ty.comptimeOnly(zcu)) {
return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{
.init_node_offset = init_src.offset.node_offset.x,
.elem_index = @intCast(runtime_index),
@@ -19468,9 +18807,8 @@ fn finishStructInit(
}
if (is_ref) {
- try struct_ty.resolveLayout(pt);
const target = zcu.getTarget();
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = result_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -19489,7 +18827,6 @@ fn finishStructInit(
.init_node_offset = init_src.offset.node_offset.x,
.elem_index = @intCast(runtime_index),
} }));
- try struct_ty.resolveStructFieldInits(pt);
const struct_val = try block.addAggregateInit(struct_ty, field_inits);
return sema.coerce(block, result_ty, struct_val, init_src);
}
@@ -19585,12 +18922,11 @@ fn structInitAnon(
break :rs runtime_index;
};
- // We treat anonymous struct types as reified types, because there are similarities:
- // * They use a form of structural equivalence, which we can easily model using a custom hash
- // * They do not have captures
- // * They immediately have their fields resolved
- // In general, other code should treat anon struct types and reified struct types identically,
- // so there's no point having a separate `InternPool.NamespaceType` field for them.
+ // We treat anonymous struct types as reified types, because there are similarities: they have
+ // no captures, and instead use a form of structural equivalence which we can easy represent by
+ // hashing the field names/types/values. They also perform layout resolution immediately. These
+ // similarities mean that other code should actually treat anon struct types and reified struct
+ // types identically anyway, so sharing the representation makes everything simpler.
const type_hash: u64 = hash: {
var hasher = std.hash.Wyhash.init(0);
hasher.update(std.mem.sliceAsBytes(types));
@@ -19599,36 +18935,36 @@ fn structInitAnon(
break :hash hasher.final();
};
const tracked_inst = try block.trackZir(inst);
- const struct_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
- .layout = .auto,
+ const struct_ty: Type = switch (try ip.getStructType(gpa, io, pt.tid, .{
.fields_len = extra_data.fields_len,
- .known_non_opv = false,
- .requires_comptime = .unknown,
+ .layout = .auto,
+ .explicit_packed_backing_type = .none,
.any_comptime_fields = any_values,
- .any_default_inits = any_values,
- .inits_resolved = true,
- .any_aligned_fields = false,
+ .any_field_defaults = any_values,
+ .any_field_aligns = false,
.key = .{ .reified = .{
.zir_index = tracked_inst,
.type_hash = type_hash,
} },
- }, false)) {
+ })) {
.wip => |wip| ty: {
errdefer wip.cancel(ip, pt.tid);
- const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index);
- wip.setName(ip, type_name.name, type_name.nav);
+ // MLUGG TODO obvs this sux
+ const anon_prefix = (try sema.createTypeName(block, .anon, "struct", inst)).anon_prefix;
+ wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{s}_{d}", .{ anon_prefix, @intFromEnum(wip.index) }, .no_embedded_nulls), .none);
const struct_type = ip.loadStructType(wip.index);
- for (names, values, 0..) |name, init_val, field_idx| {
- assert(struct_type.addFieldName(ip, name) == null);
- if (init_val != .none) struct_type.setFieldComptime(ip, field_idx);
+ for (names, values) |name, init_val| {
+ assert(wip.nextField(ip, name, init_val != .none) == null); // AstGen validated no duplicates for us
}
+ // Populating these means the type is already resolved; we don't need to add it to `zcu.outdated` or anything.
+ // That's important because type resolution relies on types being declared.
@memcpy(struct_type.field_types.get(ip), types);
- if (any_values) {
- @memcpy(struct_type.field_inits.get(ip), values);
- }
+ @memcpy(struct_type.field_defaults.get(ip), if (any_values) values else @as([]const InternPool.Index, &.{}));
+
+ try type_resolution.finishStructLayout(sema, block, src, wip.index, &struct_type);
const new_namespace_index = try pt.createNamespace(.{
.parent = block.namespace.toOptional(),
@@ -19636,7 +18972,6 @@ fn structInitAnon(
.file_scope = block.getFileScopeIndex(zcu),
.generation = zcu.generation,
});
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
if (block.ownerModule().strip) break :codegen_type;
@@ -19644,22 +18979,21 @@ fn structInitAnon(
try zcu.comp.queueJob(.{ .link_type = wip.index });
}
if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
- break :ty wip.finish(ip, new_namespace_index);
+ break :ty .fromInterned(wip.finish(ip, new_namespace_index));
},
- .existing => |ty| ty,
+ .existing => |ty| .fromInterned(ty),
};
- try sema.declareDependency(.{ .interned = struct_ty });
try sema.addTypeReferenceEntry(src, struct_ty);
_ = opt_runtime_index orelse {
- const struct_val = try pt.aggregateValue(.fromInterned(struct_ty), values);
+ const struct_val = try pt.aggregateValue(struct_ty, values);
return sema.addConstantMaybeRef(struct_val.toIntern(), is_ref);
};
if (is_ref) {
const target = zcu.getTarget();
- const alloc_ty = try pt.ptrTypeSema(.{
- .child = struct_ty,
+ const alloc_ty = try pt.ptrType(.{
+ .child = struct_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
const alloc = try block.addTy(.alloc, alloc_ty);
@@ -19672,7 +19006,7 @@ fn structInitAnon(
};
extra_index = item.end;
- const field_ptr_ty = try pt.ptrTypeSema(.{
+ const field_ptr_ty = try pt.ptrType(.{
.child = field_ty,
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -19697,7 +19031,7 @@ fn structInitAnon(
element_refs[i] = try sema.resolveInst(item.data.init);
}
- return block.addAggregateInit(.fromInterned(struct_ty), element_refs);
+ return block.addAggregateInit(struct_ty, element_refs);
}
fn zirArrayInit(
@@ -19737,17 +19071,16 @@ fn zirArrayInit(
} });
// Less inits than needed.
if (i + 2 > args.len) if (is_tuple) {
- const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern();
- if (default_val == .unreachable_value) {
+ const default_val = array_ty.structFieldDefaultValue(i, zcu) orelse {
const template = "missing tuple field with index {d}";
if (root_msg) |msg| {
try sema.errNote(src, msg, template, .{i});
} else {
root_msg = try sema.errMsg(src, template, .{i});
}
- } else {
- dest.* = Air.internedToRef(default_val);
- }
+ continue;
+ };
+ dest.* = .fromValue(default_val);
continue;
} else {
dest.* = Air.internedToRef(sentinel_val.?.toIntern());
@@ -19759,11 +19092,9 @@ fn zirArrayInit(
const elem_ty = if (is_tuple)
array_ty.fieldType(i, zcu)
else
- array_ty.elemType2(zcu);
+ array_ty.childType(zcu);
dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
if (is_tuple) {
- if (array_ty.structFieldIsComptime(i, zcu))
- try array_ty.resolveStructFieldInits(pt);
if (try array_ty.structFieldValueComptime(pt, i)) |field_val| {
const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field });
if (!field_val.eql(init_val, elem_ty, zcu)) {
@@ -19798,7 +19129,7 @@ fn zirArrayInit(
if (is_ref) {
const target = zcu.getTarget();
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = result_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -19807,7 +19138,7 @@ fn zirArrayInit(
if (is_tuple) {
for (resolved_args, 0..) |arg, i| {
- const elem_ptr_ty = try pt.ptrTypeSema(.{
+ const elem_ptr_ty = try pt.ptrType(.{
.child = array_ty.fieldType(i, zcu).toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -19820,8 +19151,8 @@ fn zirArrayInit(
return sema.makePtrConst(block, alloc);
}
- const elem_ptr_ty = try pt.ptrTypeSema(.{
- .child = array_ty.elemType2(zcu).toIntern(),
+ const elem_ptr_ty = try pt.ptrType(.{
+ .child = array_ty.childType(zcu).toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
@@ -19932,14 +19263,14 @@ fn arrayInitAnon(
if (is_ref) {
const target = sema.pt.zcu.getTarget();
- const alloc_ty = try pt.ptrTypeSema(.{
+ const alloc_ty = try pt.ptrType(.{
.child = tuple_ty.toIntern(),
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
const alloc = try block.addTy(.alloc, alloc_ty);
for (operands, 0..) |operand, i_usize| {
const i: u32 = @intCast(i_usize);
- const field_ptr_ty = try pt.ptrTypeSema(.{
+ const field_ptr_ty = try pt.ptrType(.{
.child = types[i],
.flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
});
@@ -19971,6 +19302,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name });
+ try sema.ensureLayoutResolved(aggregate_ty);
return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
}
@@ -19990,9 +19322,11 @@ fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu);
const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls);
+ try sema.ensureLayoutResolved(aggregate_ty);
return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
}
+/// Asserts that the layout of `aggregate_ty` is resolved.
fn fieldType(
sema: *Sema,
block: *Block,
@@ -20006,7 +19340,6 @@ fn fieldType(
const ip = &zcu.intern_pool;
var cur_ty = aggregate_ty;
while (true) {
- try cur_ty.resolveFields(pt);
switch (cur_ty.zigTypeTag(zcu)) {
.@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) {
.tuple_type => |tuple| {
@@ -20024,10 +19357,11 @@ fn fieldType(
},
.@"union" => {
const union_obj = zcu.typeToUnion(cur_ty).?;
- const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse
+ const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
+ const field_index = enum_obj.nameIndex(ip, field_name) orelse
return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name);
const field_ty = union_obj.field_types.get(ip)[field_index];
- return Air.internedToRef(field_ty);
+ return .fromIntern(field_ty);
},
.optional => {
// Struct/array init through optional requires the child type to not be a pointer.
@@ -20056,7 +19390,6 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
- try stack_trace_ty.resolveFields(pt);
const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());
@@ -20064,7 +19397,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
.func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) {
return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
},
- .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
+ .@"comptime", .nav_ty, .nav_val, .type_layout, .type_inits, .memoized_state => {},
}
return Air.internedToRef(try pt.intern(.{ .opt = .{
.ty = opt_ptr_stack_trace_ty.toIntern(),
@@ -20083,15 +19416,16 @@ fn zirFrame(
}
fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const zcu = sema.pt.zcu;
+ const pt = sema.pt;
+ const zcu = pt.zcu;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
const ty = try sema.resolveType(block, operand_src, inst_data.operand);
if (ty.isNoReturn(zcu)) {
return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)});
}
- const val = try ty.lazyAbiAlignment(sema.pt);
- return Air.internedToRef(val.toIntern());
+ try sema.ensureLayoutResolved(ty);
+ return .fromValue(try pt.intValue(.comptime_int, ty.abiAlignment(zcu).toByteUnits().?));
}
fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -20249,7 +19583,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- try operand_ty.resolveLayout(pt);
const enum_ty = switch (operand_ty.zigTypeTag(zcu)) {
.enum_literal => {
const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?;
@@ -20332,7 +19665,7 @@ fn zirReifySliceArgTy(
// zig fmt: on
};
- const operand_ty = try pt.ptrTypeSema(.{
+ const operand_ty = try pt.ptrType(.{
.child = in_scalar_ty.toIntern(),
.flags = .{ .size = .slice, .is_const = true },
});
@@ -20342,7 +19675,7 @@ fn zirReifySliceArgTy(
const operand_val = try sema.resolveConstDefinedValue(block, src, operand_coerced, .{ .simple = comptime_reason });
const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len);
if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
- const len = try len_val.toUnsignedIntSema(pt);
+ const len = len_val.toUnsignedInt(zcu);
return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{
.len = len,
@@ -20370,7 +19703,7 @@ fn zirReifyEnumValueSliceTy(
const operand_val = try sema.resolveConstDefinedValue(block, field_names_src, operand_coerced, .{ .simple = .enum_field_names });
const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len);
if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, field_names_src, null);
- const len = try len_val.toUnsignedIntSema(pt);
+ const len = len_val.toUnsignedInt(zcu);
return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{
.len = len,
@@ -20422,6 +19755,7 @@ fn zirReifyTuple(
if (field_ty_val.isUndef(zcu)) {
return sema.failWithUseOfUndef(block, operand_src, null);
}
+ try sema.validateTupleFieldType(block, field_ty_val.toType(), operand_src);
field_ty.* = field_ty_val.toIntern();
}
@@ -20516,7 +19850,7 @@ fn zirReifyPointer(
}
}
- return .fromType(try pt.ptrTypeSema(.{
+ return .fromType(try pt.ptrType(.{
.child = elem_ty.toIntern(),
.sentinel = if (opt_sentinel) |s| s.toIntern() else .none,
.flags = .{
@@ -20571,6 +19905,7 @@ fn zirReifyFn(
const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs });
const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty);
+ try sema.ensureLayoutResolved(ret_ty);
const fn_attrs_uncoerced = try sema.resolveInst(extra.fn_attrs);
const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src);
@@ -20595,7 +19930,8 @@ fn zirReifyFn(
param_types_src,
fn_attrs.@"callconv",
);
- if (try param_ty.comptimeOnlySema(pt)) {
+ try sema.ensureLayoutResolved(param_ty);
+ if (param_ty.comptimeOnly(zcu)) {
return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only parameter type '{f}'", .{param_ty.fmt(pt)});
}
if (param_attrs.@"noalias") {
@@ -20621,7 +19957,7 @@ fn zirReifyFn(
false,
false,
);
- if (try ret_ty.comptimeOnlySema(pt)) {
+ if (ret_ty.comptimeOnly(zcu)) {
return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only return type '{f}'", .{ret_ty.fmt(pt)});
}
@@ -20632,7 +19968,6 @@ fn zirReifyFn(
.return_type = ret_ty.toIntern(),
.cc = fn_attrs.@"callconv",
.is_var_args = fn_attrs.varargs,
- .is_generic = false,
.is_noinline = false,
}));
}
@@ -20791,8 +20126,7 @@ fn zirReifyStruct(
field_attrs_src,
.{ .simple = .struct_field_default_value },
);
- // Resolve the value so that lazy values do not create distinct types.
- break :d (try sema.resolveLazyValue(deref_val)).toIntern();
+ break :d deref_val.toIntern();
};
std.hash.autoHash(&hasher, .{
@@ -20823,36 +20157,31 @@ fn zirReifyStruct(
}
const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
- .layout = layout,
.fields_len = @intCast(fields_len),
- .known_non_opv = false,
- .requires_comptime = .unknown,
+ .layout = layout,
+ .explicit_packed_backing_type = if (backing_int_ty) |t| t.toIntern() else .none,
.any_comptime_fields = any_comptime_fields,
- .any_default_inits = any_default_inits,
- .any_aligned_fields = any_aligned_fields,
- .inits_resolved = true,
+ .any_field_defaults = any_default_inits,
+ .any_field_aligns = any_aligned_fields,
.key = .{ .reified = .{
.zir_index = tracked_inst,
.type_hash = hasher.final(),
} },
- }, false)) {
+ })) {
.wip => |wip| wip,
.existing => |ty| {
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
+ try sema.addTypeReferenceEntry(src, .fromInterned(ty));
+ return .fromIntern(ty);
},
};
errdefer wip_ty.cancel(ip, pt.tid);
- const type_name = try sema.createTypeName(
+ _ = try (try sema.createTypeName(
block,
name_strategy,
"struct",
inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
+ )).apply(&wip_ty, pt);
const wip_struct_type = ip.loadStructType(wip_ty.index);
@@ -20860,38 +20189,27 @@ fn zirReifyStruct(
const field_name_val = try field_names_arr.elemValue(pt, field_idx);
const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx);
- const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType();
-
// Don't pass a reason; first loop acts as a check that this is valid.
const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined);
- if (wip_struct_type.addFieldName(ip, field_name)) |prev_index| {
+ const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType();
+ const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
+ std.builtin.Type.StructField.Attributes,
+ "comptime",
+ ).?);
+ const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
+ std.builtin.Type.StructField.Attributes,
+ "align",
+ ).?);
+ const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
+ std.builtin.Type.StructField.Attributes,
+ "default_value_ptr",
+ ).?);
+
+ if (wip_ty.nextField(ip, field_name, field_attr_comptime.toBool())) |prev_index| {
_ = prev_index; // TODO: better source location
return sema.fail(block, field_names_src, "duplicate struct field name {f}", .{field_name.fmt(ip)});
}
- const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
- std.builtin.Type.StructField.Attributes,
- "comptime",
- ).?);
- const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
- std.builtin.Type.StructField.Attributes,
- "align",
- ).?);
- const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
- std.builtin.Type.StructField.Attributes,
- "default_value_ptr",
- ).?);
-
- if (field_attr_align.optionalValue(zcu)) |field_align_val| {
- assert(layout != .@"packed");
- const bytes = try field_align_val.toUnsignedIntSema(pt);
- const a = try sema.validateAlign(block, field_attrs_src, bytes);
- wip_struct_type.field_aligns.get(ip)[field_idx] = a;
- } else if (any_aligned_fields) {
- assert(layout != .@"packed");
- wip_struct_type.field_aligns.get(ip)[field_idx] = .none;
- }
-
const field_default: InternPool.Index = d: {
const ptr_val = field_attr_default_value_ptr.optionalValue(zcu) orelse break :d .none;
assert(any_default_inits);
@@ -20902,20 +20220,11 @@ fn zirReifyStruct(
if (deref_val.canMutateComptimeVarState(zcu)) {
return sema.failWithContainsReferenceToComptimeVar(block, field_attrs_src, field_name, "field default value", deref_val);
}
- break :d (try sema.resolveLazyValue(deref_val)).toIntern();
+ break :d deref_val.toIntern();
};
- if (field_attr_comptime.toBool()) {
- assert(layout == .auto);
- if (field_default == .none) {
- return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{});
- }
- wip_struct_type.setFieldComptime(ip, field_idx);
- }
-
- wip_struct_type.field_types.get(ip)[field_idx] = field_ty.toIntern();
- if (field_default != .none) {
- wip_struct_type.field_inits.get(ip)[field_idx] = field_default;
+ if (field_attr_comptime.toBool() and field_default == .none) {
+ return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{});
}
switch (field_ty.zigTypeTag(zcu)) {
@@ -20945,32 +20254,55 @@ fn zirReifyStruct(
break :msg msg;
});
},
- .@"packed" => if (!try sema.validatePackedType(field_ty)) {
+ .@"packed" => if (!field_ty.packable(zcu)) {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(field_types_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
errdefer msg.destroy(gpa);
- try sema.explainWhyTypeIsNotPacked(msg, field_types_src, field_ty);
+ try sema.explainWhyTypeIsNotPackable(msg, field_types_src, field_ty);
try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg;
});
},
}
+
+ wip_struct_type.field_types.get(ip)[field_idx] = field_ty.toIntern();
+ if (field_default != .none) {
+ wip_struct_type.field_defaults.get(ip)[field_idx] = field_default;
+ }
+
+ if (field_attr_align.optionalValue(zcu)) |field_align_val| {
+ assert(layout != .@"packed");
+ const bytes = field_align_val.toUnsignedInt(zcu);
+ const a = try sema.validateAlign(block, field_attrs_src, bytes);
+ wip_struct_type.field_aligns.get(ip)[field_idx] = a;
+ } else if (any_aligned_fields) {
+ assert(layout != .@"packed");
+ wip_struct_type.field_aligns.get(ip)[field_idx] = .none;
+ }
}
if (layout == .@"packed") {
- var fields_bit_sum: u64 = 0;
- for (0..wip_struct_type.field_types.len) |field_idx| {
+ var field_bits: u64 = 0;
+ for (0..fields_len) |field_idx| {
const field_ty: Type = .fromInterned(wip_struct_type.field_types.get(ip)[field_idx]);
- try field_ty.resolveLayout(pt);
- fields_bit_sum += field_ty.bitSize(zcu);
- }
- if (backing_int_ty) |ty| {
- try sema.checkBackingIntType(block, src, ty, fields_bit_sum);
- wip_struct_type.setBackingIntType(ip, io, ty.toIntern());
- } else {
- const ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
- wip_struct_type.setBackingIntType(ip, io, ty.toIntern());
+ try sema.ensureLayoutResolved(field_ty);
+ field_bits += field_ty.bitSize(zcu);
}
+ try type_resolution.resolvePackedStructBackingInt(
+ sema,
+ block,
+ field_bits,
+ .fromInterned(wip_ty.index),
+ &wip_struct_type,
+ );
+ } else {
+ try type_resolution.finishStructLayout(
+ sema,
+ block,
+ src,
+ wip_ty.index,
+ &wip_struct_type,
+ );
}
const new_namespace_index = try pt.createNamespace(.{
@@ -20980,16 +20312,13 @@ fn zirReifyStruct(
.generation = zcu.generation,
});
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
}
- try sema.declareDependency(.{ .interned = wip_ty.index });
- try sema.addTypeReferenceEntry(src, wip_ty.index);
+ try sema.addTypeReferenceEntry(src, .fromInterned(wip_ty.index));
if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
return .fromIntern(wip_ty.finish(ip, new_namespace_index));
}
@@ -21134,62 +20463,52 @@ fn zirReifyUnion(
}
// Some basic validation to avoid a bogus `getUnionType` call...
- const explicit_tag_ty: ?Type = if (arg_ty_val.optionalValue(zcu)) |arg_ty| ty: {
+ const explicit_tag_ty: ?Type, const explicit_packed_backing_type: ?Type = ty: {
+ const arg_ty = arg_ty_val.optionalValue(zcu) orelse break :ty .{ null, null };
switch (layout) {
- .@"extern", .@"packed" => return sema.fail(block, arg_ty_src, "{t} union does not support enum tag type", .{layout}),
- .auto => {},
+ .@"extern" => return sema.fail(block, arg_ty_src, "extern union does not support enum tag type", .{}),
+ .@"packed" => break :ty .{ null, arg_ty.toType() },
+ .auto => break :ty .{ arg_ty.toType(), null },
}
- break :ty arg_ty.toType();
- } else null;
+ };
if (any_aligned_fields and layout == .@"packed") {
return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{});
}
const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{
- .flags = .{
- .layout = layout,
- .status = .none,
- .runtime_tag = rt: {
- if (explicit_tag_ty != null) break :rt .tagged;
- if (layout == .auto and block.wantSafeTypes()) break :rt .safety;
- break :rt .none;
- },
- .any_aligned_fields = any_aligned_fields,
- .requires_comptime = .unknown,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
- .alignment = .none,
- },
.fields_len = @intCast(fields_len),
- .enum_tag_ty = .none, // set later because not yet validated
- .field_types = &.{}, // set later
- .field_aligns = &.{}, // set later
+ .layout = layout,
+ .explicit_packed_backing_type = if (explicit_packed_backing_type) |t| t.toIntern() else .none,
+ .runtime_tag = rt: {
+ if (explicit_tag_ty != null) break :rt .tagged;
+ if (layout == .auto and block.wantSafeTypes()) break :rt .safety;
+ break :rt .none;
+ },
+ .have_explicit_enum_tag = explicit_tag_ty != null,
+ .any_field_aligns = any_aligned_fields,
.key = .{ .reified = .{
.zir_index = tracked_inst,
.type_hash = hasher.final(),
} },
- }, false)) {
+ })) {
.wip => |wip| wip,
.existing => |ty| {
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
+ try sema.addTypeReferenceEntry(src, .fromInterned(ty));
+ return .fromIntern(ty);
},
};
errdefer wip_ty.cancel(ip, pt.tid);
- const type_name = try sema.createTypeName(
+ const type_name = try (try sema.createTypeName(
block,
name_strategy,
"union",
inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
+ )).apply(&wip_ty, pt);
const loaded_union = ip.loadUnionType(wip_ty.index);
- const enum_tag_ty, const has_explicit_tag = if (explicit_tag_ty) |enum_tag_ty| tag: {
+ const generated_tag_ty: InternPool.Index = if (explicit_tag_ty) |enum_tag_ty| generated_tag: {
if (enum_tag_ty.zigTypeTag(zcu) != .@"enum") {
return sema.fail(block, arg_ty_src, "tag type must be an enum type", .{});
}
@@ -21227,26 +20546,67 @@ fn zirReifyUnion(
try sema.addDeclaredHereNote(msg, enum_tag_ty);
break :msg msg;
});
- break :tag .{ enum_tag_ty.toIntern(), true };
- } else tag: {
- // We must track field names and set up the tag type ourselves.
- var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
- try field_names.ensureTotalCapacity(sema.arena, fields_len);
+ wip_ty.setTagType(ip, enum_tag_ty.toIntern());
+ break :generated_tag .none;
+ } else generated_tag: {
+ // Generate the union's hypothetical tag type.
+ const wip_tag_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
+ .fields_len = @intCast(fields_len),
+ .explicit_int_tag_type = .none,
+ .nonexhaustive = false,
+ .key = .{ .generated_union_tag = wip_ty.index },
+ })) {
+ .existing => unreachable, // enum type is keyed on this union type which we're only just creating
+ .wip => |wip_tag_ty| wip_tag_ty,
+ };
+ errdefer wip_tag_ty.cancel(ip, pt.tid);
+ // Set its name based on the union's name
+ _ = wip_tag_ty.setName(ip, try ip.getOrPutStringFmt(
+ gpa,
+ io,
+ pt.tid,
+ "@typeInfo({f}).@\"union\".tag_type.?",
+ .{type_name.fmt(ip)},
+ .no_embedded_nulls,
+ ), .none);
+
+ // Populate its fields (and report any duplicates)
for (0..fields_len) |field_idx| {
const field_name_val = try field_names_arr.elemValue(pt, field_idx);
// Don't pass a reason; first loop acts as a check that this is valid.
const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined);
- const gop = field_names.getOrPutAssumeCapacity(field_name);
- if (gop.found_existing) {
- // TODO: better source location
- return sema.fail(block, field_names_src, "duplicate union field {f}", .{field_name.fmt(ip)});
- }
+ if (wip_tag_ty.nextField(ip, field_name, false)) |prev_field_idx| return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(src, "duplicate union field '{f}' at index '{d}", .{ field_name.fmt(ip), field_idx });
+ errdefer msg.destroy(gpa);
+ try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_idx});
+ break :msg msg;
+ });
}
- const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name);
- break :tag .{ enum_tag_ty, false };
+
+ // Populate the enum tag type's *integer* tag type
+ wip_tag_ty.setTagType(ip, int_tag_ty: {
+ // Infer the int tag type from the field count
+ const bits = Type.smallestUnsignedBits(fields_len -| 1);
+ break :int_tag_ty (try pt.intType(.unsigned, bits)).toIntern();
+ });
+
+ // Lastly, it needs a dummy namespace
+ const enum_tag_type_namespace = try pt.createNamespace(.{
+ .parent = block.namespace.toOptional(),
+ .owner_type = wip_tag_ty.index,
+ .file_scope = block.getFileScopeIndex(zcu),
+ .generation = zcu.generation,
+ });
+ errdefer pt.destroyNamespace(enum_tag_type_namespace);
+
+ wip_ty.setTagType(ip, wip_tag_ty.index);
+
+ break :generated_tag wip_tag_ty.finish(ip, enum_tag_type_namespace);
};
- errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error
+ // If we fail to create the union type, we must delete the generated enum tag type, since it
+ // would hold a reference to the deleted union.
+ errdefer if (generated_tag_ty != .none) ip.remove(pt.tid, generated_tag_ty);
for (0..fields_len) |field_idx| {
const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType();
@@ -21279,12 +20639,12 @@ fn zirReifyUnion(
break :msg msg;
});
},
- .@"packed" => if (!try sema.validatePackedType(field_ty)) {
+ .@"packed" => if (!field_ty.packable(zcu)) {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(field_types_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
errdefer msg.destroy(gpa);
- try sema.explainWhyTypeIsNotPacked(msg, field_types_src, field_ty);
+ try sema.explainWhyTypeIsNotPackable(msg, field_types_src, field_ty);
try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg;
@@ -21303,8 +20663,24 @@ fn zirReifyUnion(
}
}
- loaded_union.setTagType(ip, io, enum_tag_ty);
- loaded_union.setStatus(ip, io, .have_field_types);
+ if (layout == .@"packed") {
+ try type_resolution.resolvePackedUnionBackingInt(
+ sema,
+ block,
+ .fromInterned(wip_ty.index),
+ &loaded_union,
+ true,
+ );
+ } else {
+ try type_resolution.finishUnionLayout(
+ sema,
+ block,
+ src,
+ wip_ty.index,
+ &loaded_union,
+ explicit_tag_ty orelse .fromInterned(generated_tag_ty),
+ );
+ }
const new_namespace_index = try pt.createNamespace(.{
.parent = block.namespace.toOptional(),
@@ -21313,17 +20689,16 @@ fn zirReifyUnion(
.generation = zcu.generation,
});
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
}
- try sema.declareDependency(.{ .interned = wip_ty.index });
- try sema.addTypeReferenceEntry(src, wip_ty.index);
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
+ try sema.addTypeReferenceEntry(src, .fromInterned(wip_ty.index));
+ if (zcu.comp.debugIncremental()) {
+ try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
+ }
return .fromIntern(wip_ty.finish(ip, new_namespace_index));
}
@@ -21436,86 +20811,84 @@ fn zirReifyEnum(
}
const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
- .has_values = true,
- .tag_mode = if (nonexhaustive) .nonexhaustive else .explicit,
.fields_len = @intCast(fields_len),
+ .explicit_int_tag_type = tag_ty.toIntern(),
+ .nonexhaustive = nonexhaustive,
.key = .{ .reified = .{
.zir_index = tracked_inst,
.type_hash = hasher.final(),
} },
- }, false)) {
+ })) {
.wip => |wip| wip,
.existing => |ty| {
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
+ try sema.addTypeReferenceEntry(src, .fromInterned(ty));
return .fromIntern(ty);
},
};
- var done = false;
- errdefer if (!done) wip_ty.cancel(ip, pt.tid);
+ errdefer wip_ty.cancel(ip, pt.tid);
- const type_name = try sema.createTypeName(
+ _ = try (try sema.createTypeName(
block,
name_strategy,
"enum",
inst,
- wip_ty.index,
- );
- wip_ty.setName(ip, type_name.name, type_name.nav);
-
- const new_namespace_index = try pt.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .owner_type = wip_ty.index,
- .file_scope = block.getFileScopeIndex(zcu),
- .generation = zcu.generation,
- });
-
- try sema.declareDependency(.{ .interned = wip_ty.index });
- try sema.addTypeReferenceEntry(src, wip_ty.index);
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- wip_ty.prepare(ip, new_namespace_index);
- wip_ty.setTagTy(ip, tag_ty.toIntern());
- done = true;
+ )).apply(&wip_ty, pt);
for (0..fields_len) |field_idx| {
const field_name_val = try field_names_arr.elemValue(pt, field_idx);
// Don't pass a reason; first loop acts as a check that this is valid.
const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined);
+ if (wip_ty.nextField(ip, field_name, false)) |prev_field_idx| return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(field_names_src, "duplicate enum field '{f}' at index '{d}'", .{ field_name.fmt(ip), field_idx });
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_names_src, msg, "previous field at index '{d}'", .{prev_field_idx});
+ break :msg msg;
+ });
+ }
+ const enum_obj = ip.loadEnumType(wip_ty.index);
+ const field_value_map = enum_obj.field_value_map.unwrap().?;
+ for (0..fields_len) |field_idx| {
const field_val = try field_values_arr.elemValue(pt, field_idx);
-
- if (wip_ty.nextField(ip, field_name, field_val.toIntern())) |conflict| {
- return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) {
- .name => msg: {
- const msg = try sema.errMsg(field_names_src, "duplicate enum field '{f}'", .{field_name.fmt(ip)});
- errdefer msg.destroy(gpa);
- _ = conflict.prev_field_idx; // TODO: this note is incorrect
- try sema.errNote(field_names_src, msg, "other field here", .{});
- break :msg msg;
- },
- .value => msg: {
- const msg = try sema.errMsg(field_values_src, "enum tag value {f} already taken", .{field_val.fmtValueSema(pt, sema)});
- errdefer msg.destroy(gpa);
- _ = conflict.prev_field_idx; // TODO: this note is incorrect
- try sema.errNote(field_values_src, msg, "other enum tag value here", .{});
- break :msg msg;
- },
+ const field_values = enum_obj.field_values.get(ip);
+ field_values[field_idx] = field_val.toIntern();
+ const adapter: InternPool.Index.Adapter = .{ .indexes = field_values[0..field_idx] };
+ const gop = field_value_map.get(ip).getOrPutAssumeCapacityAdapted(field_val.toIntern(), adapter);
+ if (gop.found_existing) return sema.failWithOwnedErrorMsg(block, msg: {
+ const field_names = enum_obj.field_names.get(ip);
+ const this_field_name = field_names[field_idx];
+ const prev_field_name = field_names[gop.index];
+ const msg = try sema.errMsg(field_names_src, "duplicate enum tag value '{f}' in field '{f}'", .{
+ field_val.fmtValueSema(pt, sema),
+ this_field_name.fmt(ip),
});
- }
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_names_src, msg, "previous usage in field '{f}'", .{prev_field_name.fmt(ip)});
+ break :msg msg;
+ });
}
if (nonexhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) {
return sema.fail(block, src, "non-exhaustive enum specified every value", .{});
}
+ const new_namespace_index = try pt.createNamespace(.{
+ .parent = block.namespace.toOptional(),
+ .owner_type = wip_ty.index,
+ .file_scope = block.getFileScopeIndex(zcu),
+ .generation = zcu.generation,
+ });
+
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
if (block.ownerModule().strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
}
- return Air.internedToRef(wip_ty.index);
+
+ try sema.addTypeReferenceEntry(src, .fromInterned(wip_ty.index));
+ if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
+ return .fromIntern(wip_ty.finish(ip, new_namespace_index));
}
fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
@@ -21573,7 +20946,8 @@ fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
try sema.requireRuntimeBlock(block, src, null);
- return block.addUnOp(.c_va_end, va_list_ref);
+ _ = try block.addUnOp(.c_va_end, va_list_ref);
+ return .void_value;
}
fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
@@ -21683,8 +21057,20 @@ fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
_ = try sema.checkIntType(block, operand_src, operand_scalar_ty);
if (try sema.resolveValue(operand)) |operand_val| {
- const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, pt, .sema);
- return Air.internedToRef(result_val.toIntern());
+ if (operand_val.isUndef(zcu)) return .fromValue(try pt.undefValue(dest_ty));
+ if (dest_ty.zigTypeTag(zcu) != .vector) {
+ return .fromValue(try pt.floatValue(dest_ty, operand_val.toFloat(f128, zcu)));
+ }
+ const dest_elems = try sema.arena.alloc(InternPool.Index, dest_ty.vectorLen(zcu));
+ for (dest_elems, 0..) |*out_elem, elem_idx| {
+ const orig_elem = try operand_val.elemValue(pt, elem_idx);
+ const casted_elem = if (orig_elem.isUndef(zcu))
+ try pt.undefValue(dest_scalar_ty)
+ else
+ try pt.floatValue(dest_scalar_ty, orig_elem.toFloat(f128, zcu));
+ out_elem.* = casted_elem.toIntern();
+ }
+ return .fromValue(try pt.aggregateValue(dest_ty, dest_elems));
} else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) {
return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float });
}
@@ -21719,8 +21105,11 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const ptr_ty = dest_ty.scalarType(zcu);
try sema.checkPtrType(block, src, ptr_ty, true);
- const elem_ty = ptr_ty.elemType2(zcu);
- const ptr_align = try ptr_ty.ptrAlignmentSema(pt);
+ const elem_ty = ptr_ty.nullablePtrElem(zcu);
+
+ // We'll need to validate the pointer alignment.
+ try sema.ensureLayoutResolved(elem_ty);
+ const ptr_align = ptr_ty.ptrAlignment(zcu);
if (ptr_ty.isSlice(zcu)) {
const msg = msg: {
@@ -21746,18 +21135,9 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
}
return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
}
- if (try ptr_ty.comptimeOnlySema(pt)) {
- return sema.failWithOwnedErrorMsg(block, msg: {
- const msg = try sema.errMsg(src, "pointer to comptime-only type '{f}' must be comptime-known, but operand is runtime-known", .{ptr_ty.fmt(pt)});
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsComptime(msg, src, ptr_ty);
- break :msg msg;
- });
- }
try sema.requireRuntimeBlock(block, src, operand_src);
try sema.checkLogicalPtrOperation(block, src, ptr_ty);
- if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) {
+ if (block.wantSafety()) {
if (!ptr_ty.isAllowzeroPtr(zcu)) {
const is_non_zero = if (is_vector) all_non_zero: {
const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
@@ -21804,7 +21184,7 @@ fn ptrFromIntVal(
}
return sema.failWithUseOfUndef(block, operand_src, vec_idx);
}
- const addr = try operand_val.toUnsignedIntSema(pt);
+ const addr = operand_val.toUnsignedInt(zcu);
if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0)
return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)});
if (addr != 0 and ptr_align != .none) {
@@ -22043,8 +21423,8 @@ fn ptrCastFull(
const src_info = operand_ty.ptrInfo(zcu);
const dest_info = dest_ty.ptrInfo(zcu);
- try Type.fromInterned(src_info.child).resolveLayout(pt);
- try Type.fromInterned(dest_info.child).resolveLayout(pt);
+ try sema.ensureLayoutResolved(.fromInterned(src_info.child));
+ try sema.ensureLayoutResolved(.fromInterned(dest_info.child));
const DestSliceLen = union(enum) {
undef,
@@ -22079,9 +21459,9 @@ fn ptrCastFull(
.pointer => operand_val,
else => unreachable,
};
- const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern())));
- if (slice_len_resolved.isUndef(zcu)) break :len .undef;
- break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) };
+ const slice_len: Value = .fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()));
+ if (slice_len.isUndef(zcu)) break :len .undef;
+ break :src .{ .fromInterned(src_info.child), slice_len.toUnsignedInt(zcu) };
},
.many, .c => {
return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)});
@@ -22395,7 +21775,7 @@ fn ptrCastFull(
};
if (dest_align.compare(.gt, src_align)) {
- if (try ptr_val.getUnsignedIntSema(pt)) |addr| {
+ if (ptr_val.getUnsignedInt(zcu)) |addr| {
const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask|
addr & mask
else
@@ -22464,7 +21844,7 @@ fn ptrCastFull(
// Now, do an addrspace cast if necessary!
if (!flags.addrspace_cast) break :ptr pre_addrspace_cast;
- const intermediate_ptr_ty = try pt.ptrTypeSema(info: {
+ const intermediate_ptr_ty = try pt.ptrType(info: {
var info = src_info;
info.flags.address_space = dest_info.flags.address_space;
break :info info;
@@ -22638,7 +22018,7 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
const dest_ty = blk: {
- const dest_ty = try pt.ptrTypeSema(ptr_info);
+ const dest_ty = try pt.ptrType(ptr_info);
if (operand_ty.zigTypeTag(zcu) == .optional) {
break :blk try pt.optionalType(dest_ty.toIntern());
}
@@ -22678,48 +22058,24 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return sema.coerce(block, dest_ty, operand, operand_src);
}
+ if (try dest_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
+
const dest_info = dest_scalar_ty.intInfo(zcu);
- if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
- return Air.internedToRef(val.toIntern());
- }
-
if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) {
const operand_info = operand_ty.intInfo(zcu);
- if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
- return Air.internedToRef(val.toIntern());
- }
if (operand_info.signedness != dest_info.signedness) {
return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{
@tagName(dest_info.signedness), operand_ty.fmt(pt),
});
}
- switch (std.math.order(dest_info.bits, operand_info.bits)) {
- .gt => {
- const msg = msg: {
- const msg = try sema.errMsg(
- src,
- "destination type '{f}' has more bits than source type '{f}'",
- .{ dest_ty.fmt(pt), operand_ty.fmt(pt) },
- );
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(src, msg, "destination type has {d} bits", .{
- dest_info.bits,
- });
- try sema.errNote(operand_src, msg, "operand type has {d} bits", .{
- operand_info.bits,
- });
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- },
- .eq => return operand,
- .lt => {},
+ if (dest_info.bits >= operand_info.bits) {
+ return sema.coerce(block, dest_ty, operand, operand_src);
}
}
- if (try sema.resolveValueResolveLazy(operand)) |val| {
+ if (try sema.resolveValue(operand)) |val| {
const result_val = try arith.truncate(sema, val, operand_ty, dest_ty, dest_info.signedness, dest_info.bits);
return Air.internedToRef(result_val.toIntern());
}
@@ -22745,10 +22101,6 @@ fn zirBitCount(
_ = try sema.checkIntOrVector(block, operand, operand_src);
const bits = operand_ty.intInfo(zcu).bits;
- if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
- return Air.internedToRef(val.toIntern());
- }
-
const result_scalar_ty = try pt.smallestUnsignedInt(bits);
switch (operand_ty.zigTypeTag(zcu)) {
.vector => {
@@ -22774,7 +22126,7 @@ fn zirBitCount(
}
},
.int => {
- if (try sema.resolveValueResolveLazy(operand)) |val| {
+ if (try sema.resolveValue(operand)) |val| {
if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty);
return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu));
} else {
@@ -22803,9 +22155,6 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.{ scalar_ty.fmt(pt), bits },
);
}
- if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
- return .fromValue(val);
- }
if (try sema.resolveValue(operand)) |operand_val| {
return .fromValue(try arith.byteSwap(sema, operand_val, operand_ty));
}
@@ -22819,9 +22168,6 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const operand_ty = sema.typeOf(operand);
_ = try sema.checkIntOrVector(block, operand, operand_src);
- if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
- return .fromValue(val);
- }
if (try sema.resolveValue(operand)) |operand_val| {
return .fromValue(try arith.bitReverse(sema, operand_val, operand_ty));
}
@@ -22849,10 +22195,11 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
const ty = try sema.resolveType(block, ty_src, extra.lhs);
const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name });
+ try sema.ensureLayoutResolved(ty);
+
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- try ty.resolveLayout(pt);
switch (ty.zigTypeTag(zcu)) {
.@"struct" => {},
else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}),
@@ -23126,7 +22473,7 @@ fn checkAtomicPtrOperand(
const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) {
.pointer => ptr_ty.ptrInfo(zcu),
else => {
- const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data);
+ const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data);
_ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
unreachable;
},
@@ -23136,7 +22483,7 @@ fn checkAtomicPtrOperand(
wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
- const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data);
+ const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data);
const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
return casted_ptr;
@@ -23470,11 +22817,8 @@ fn zirCmpxchg(
const result_ty = try pt.optionalType(elem_ty.toIntern());
// special case zero bit types
- if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
- return Air.internedToRef((try pt.intern(.{ .opt = .{
- .ty = result_ty.toIntern(),
- .val = .none,
- } })));
+ if (try elem_ty.onePossibleValue(pt) != null) {
+ return .fromValue(try pt.nullValue(result_ty));
}
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
@@ -23537,11 +22881,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu));
- if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
- return Air.internedToRef(val.toIntern());
- }
-
- // We also need this case because `[0:s]T` is not OPV.
+ // If the length is 0, the result is comptime-known even if the operand isn't.
if (len == 0) return .fromValue(try pt.aggregateValue(dest_ty, &.{}));
const maybe_sentinel = dest_ty.sentinel(zcu);
@@ -23733,7 +23073,7 @@ fn analyzeShuffle(
continue;
}
// Safe because mask elements are `i32` and we already checked for undef:
- const raw = (try sema.resolveLazyValue(mask_val)).toSignedInt(zcu);
+ const raw = mask_val.toSignedInt(zcu);
if (raw >= 0) {
const idx: u32 = @intCast(raw);
a_used = true;
@@ -23938,6 +23278,8 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
+ try sema.ensureLayoutResolved(elem_ty);
+
switch (order) {
.release, .acq_rel => {
return sema.fail(
@@ -23950,9 +23292,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
else => {},
}
- if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
- return Air.internedToRef(val.toIntern());
- }
+ if (try elem_ty.onePossibleValue(sema.pt)) |opv| return .fromValue(opv);
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
@@ -24009,9 +23349,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
}
// special case zero bit types
- if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
- return Air.internedToRef(val.toIntern());
- }
+ if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
const maybe_operand_val = try sema.resolveValue(operand);
@@ -24260,11 +23598,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)});
}
const parent_ty: Type = .fromInterned(parent_ptr_info.child);
+ try sema.ensureLayoutResolved(parent_ty);
switch (parent_ty.zigTypeTag(zcu)) {
.@"struct", .@"union" => {},
else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}),
}
- try parent_ty.resolveLayout(pt);
const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
const field_index = switch (parent_ty.zigTypeTag(zcu)) {
@@ -24293,7 +23631,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
var actual_parent_ptr_info: InternPool.Key.PtrType = .{
.child = parent_ty.toIntern(),
.flags = .{
- .alignment = try parent_ptr_ty.ptrAlignmentSema(pt),
+ .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,
@@ -24305,7 +23643,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
var actual_field_ptr_info: InternPool.Key.PtrType = .{
.child = field_ty.toIntern(),
.flags = .{
- .alignment = try field_ptr_ty.ptrAlignmentSema(pt),
+ .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,
@@ -24315,23 +23653,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
};
switch (parent_ty.containerLayout(zcu)) {
.auto => {
- actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(
- if (zcu.typeToStruct(parent_ty)) |struct_obj|
- try field_ty.structFieldAlignmentSema(
- struct_obj.fieldAlign(ip, field_index),
- struct_obj.layout,
- pt,
- )
- else if (zcu.typeToUnion(parent_ty)) |union_obj|
- try field_ty.unionFieldAlignmentSema(
- union_obj.fieldAlign(ip, field_index),
- union_obj.flagsUnordered(ip).layout,
- pt,
- )
- else
- actual_field_ptr_info.flags.alignment,
- );
-
+ 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 };
},
@@ -24357,9 +23679,9 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
},
}
- const actual_field_ptr_ty = try pt.ptrTypeSema(actual_field_ptr_info);
+ 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.ptrTypeSema(actual_parent_ptr_info);
+ const actual_parent_ptr_ty = try pt.ptrType(actual_parent_ptr_info);
const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: {
switch (parent_ty.zigTypeTag(zcu)) {
@@ -24590,7 +23912,7 @@ fn analyzeMinMax(
const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu);
const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) {
.comptime_int => s: {
- const val = (try sema.resolveValueResolveLazy(operand)).?;
+ const val = (try sema.resolveValue(operand)).?;
if (val.isUndef(zcu)) break :s .none;
break :s .{ .int = .{
.all_comptime_int = true,
@@ -24609,7 +23931,7 @@ fn analyzeMinMax(
// (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only
// use the input *types* to determine the result type.
const min: Value, const max: Value = bounds: {
- if (try sema.resolveValueResolveLazy(operand)) |operand_val| {
+ if (try sema.resolveValue(operand)) |operand_val| {
if (vector_len) |len| {
var min = try operand_val.elemValue(pt, 0);
var max = min;
@@ -24696,6 +24018,9 @@ fn analyzeMinMax(
.child = intermediate_scalar_ty.toIntern(),
}) else intermediate_scalar_ty;
+ // We might have refined all the way down to an OPV type---check now.
+ if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
+
// This value, if not `null`, will have type `intermediate_ty`.
const comptime_part: ?Value = ct: {
// Contains the comptime-known scalar result values.
@@ -24712,7 +24037,7 @@ fn analyzeMinMax(
var opt_runtime_src: ?LazySrcLoc = null;
for (operands, operand_srcs) |operand, operand_src| {
- const operand_val = try sema.resolveValueResolveLazy(operand) orelse {
+ const operand_val = try sema.resolveValue(operand) orelse {
if (opt_runtime_src == null) opt_runtime_src = operand_src;
continue;
};
@@ -24819,7 +24144,7 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A
// Already an array pointer.
return ptr;
}
- const new_ty = try pt.ptrTypeSema(.{
+ const new_ty = try pt.ptrType(.{
.child = (try pt.arrayType(.{
.len = len,
.sentinel = info.sentinel,
@@ -24883,6 +24208,9 @@ fn zirMemcpy(
const dest_elem_ty = dest_ty.indexablePtrElem(zcu);
const src_elem_ty = src_ty.indexablePtrElem(zcu);
+ try sema.ensureLayoutResolved(dest_elem_ty);
+ try sema.ensureLayoutResolved(src_elem_ty);
+
const imc = try sema.coerceInMemoryAllowed(
block,
dest_elem_ty,
@@ -24946,13 +24274,13 @@ fn zirMemcpy(
}
zero_bit: {
- const src_comptime = try src_elem_ty.comptimeOnlySema(pt);
- const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt);
+ const src_comptime = src_elem_ty.comptimeOnly(zcu);
+ const dest_comptime = dest_elem_ty.comptimeOnly(zcu);
assert(src_comptime == dest_comptime); // IMC
if (src_comptime) break :zero_bit;
- const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt);
- const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt);
+ const src_has_bits = src_elem_ty.hasRuntimeBits(zcu);
+ const dest_has_bits = dest_elem_ty.hasRuntimeBits(zcu);
assert(src_has_bits == dest_has_bits); // IMC
if (src_has_bits) break :zero_bit;
@@ -24968,7 +24296,7 @@ fn zirMemcpy(
const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val;
const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val;
- const len_u64 = try len_val.?.toUnsignedIntSema(pt);
+ const len_u64 = len_val.?.toUnsignedInt(zcu);
if (check_aliasing) {
if (Value.doPointersOverlap(
@@ -25018,7 +24346,7 @@ fn zirMemcpy(
var new_dest_ptr = dest_ptr;
var new_src_ptr = src_ptr;
if (len_val) |val| {
- const len = try val.toUnsignedIntSema(pt);
+ const len = val.toUnsignedInt(zcu);
if (len == 0) {
// This AIR instruction guarantees length > 0 if it is comptime-known.
return;
@@ -25067,7 +24395,7 @@ fn zirMemcpy(
assert(dest_manyptr_ty_key.flags.size == .one);
dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
dest_manyptr_ty_key.flags.size = .many;
- break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(dest_manyptr_ty_key), new_dest_ptr, dest_src);
+ break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src);
} else new_dest_ptr;
const new_src_ptr_ty = sema.typeOf(new_src_ptr);
@@ -25078,7 +24406,7 @@ fn zirMemcpy(
assert(src_manyptr_ty_key.flags.size == .one);
src_manyptr_ty_key.child = src_elem_ty.toIntern();
src_manyptr_ty_key.flags.size = .many;
- break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(src_manyptr_ty_key), new_src_ptr, src_src);
+ break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(src_manyptr_ty_key), new_src_ptr, src_src);
} else new_src_ptr;
// ok1: dest >= src + len
@@ -25148,7 +24476,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const runtime_src = rs: {
const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), dest_src);
const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src;
- const len_u64 = try len_val.toUnsignedIntSema(pt);
+ const len_u64 = len_val.toUnsignedInt(zcu);
const len = try sema.usizeCast(block, dest_src, len_u64);
if (len == 0) {
// This AIR instruction guarantees length > 0 if it is comptime-known.
@@ -25436,7 +24764,7 @@ fn resolvePrefetchOptions(
return std.builtin.PrefetchOptions{
.rw = try sema.interpretBuiltinType(block, rw_src, rw_val, std.builtin.PrefetchOptions.Rw),
- .locality = @intCast(try locality_val.toUnsignedIntSema(pt)),
+ .locality = @intCast(locality_val.toUnsignedInt(zcu)),
.cache = try sema.interpretBuiltinType(block, cache_src, cache_val, std.builtin.PrefetchOptions.Cache),
};
}
@@ -25626,7 +24954,7 @@ fn zirBuiltinExtern(
// So, for now, just use our containing `declaration`.
.zir_index = switch (sema.owner.unwrap()) {
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
- .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
+ .type_layout, .type_inits => |owner_ty| Type.fromInterned(owner_ty).typeDeclInstAllowGeneratedTag(zcu).?,
.memoized_state => unreachable,
.nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
.func => |func| zir_index: {
@@ -25839,7 +25167,8 @@ fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src:
}
}
-/// Emit a compile error if type cannot be used for a runtime variable.
+/// Emit a compile error if `var_ty` cannot be used for a runtime variable.
+/// Asserts that the layout of `var_ty` is already resolved.
pub fn validateVarType(
sema: *Sema,
block: *Block,
@@ -25849,6 +25178,7 @@ pub fn validateVarType(
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
+ var_ty.assertHasLayout(zcu);
if (is_extern) {
if (!try sema.validateExternType(var_ty, .other)) {
const msg = msg: {
@@ -25870,7 +25200,7 @@ pub fn validateVarType(
}
}
- if (!try var_ty.comptimeOnlySema(pt)) return;
+ if (!var_ty.comptimeOnly(zcu)) return;
const msg = msg: {
const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)});
@@ -25886,49 +25216,28 @@ pub fn validateVarType(
return sema.failWithOwnedErrorMsg(block, msg);
}
-const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
-
fn explainWhyTypeIsComptime(
sema: *Sema,
msg: *Zcu.ErrorMsg,
- src_loc: LazySrcLoc,
+ src: LazySrcLoc,
ty: Type,
-) CompileError!void {
- var type_set = TypeSet{};
- defer type_set.deinit(sema.gpa);
-
- try ty.resolveFully(sema.pt);
- return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set);
-}
-
-fn explainWhyTypeIsComptimeInner(
- sema: *Sema,
- msg: *Zcu.ErrorMsg,
- src_loc: LazySrcLoc,
- ty: Type,
- type_set: *TypeSet,
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
+ assert(ty.comptimeOnly(zcu));
switch (ty.zigTypeTag(zcu)) {
.bool,
.int,
.float,
.error_set,
- .@"enum",
.frame,
.@"anyframe",
.void,
- => return,
-
- .@"fn" => {
- try sema.errNote(src_loc, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)});
- },
-
- .type => {
- try sema.errNote(src_loc, msg, "types are not available at runtime", .{});
- },
+ .@"enum",
+ .@"opaque",
+ .pointer,
+ => unreachable, // not comptime-only
.comptime_float,
.comptime_int,
@@ -25936,78 +25245,53 @@ fn explainWhyTypeIsComptimeInner(
.noreturn,
.undefined,
.null,
- => return,
+ => return, // no explanation needed
- .@"opaque" => {
- try sema.errNote(src_loc, msg, "opaque type '{f}' has undefined size", .{ty.fmt(pt)});
- },
+ .array, .vector => try sema.explainWhyTypeIsComptime(msg, src, ty.childType(zcu)),
+ .optional => try sema.explainWhyTypeIsComptime(msg, src, ty.optionalChild(zcu)),
+ .error_union => try sema.explainWhyTypeIsComptime(msg, src, ty.errorUnionPayload(zcu)),
- .array, .vector => {
- try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set);
- },
- .pointer => {
- const elem_ty = ty.elemType2(zcu);
- if (elem_ty.zigTypeTag(zcu) == .@"fn") {
- const fn_info = zcu.typeToFunc(elem_ty).?;
- if (fn_info.is_generic) {
- try sema.errNote(src_loc, msg, "function is generic", .{});
- }
- switch (fn_info.cc) {
- .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}),
- else => {},
- }
- if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) {
- try sema.errNote(src_loc, msg, "function has a comptime-only return type", .{});
- }
- return;
- }
- try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set);
- },
-
- .optional => {
- try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(zcu), type_set);
- },
- .error_union => {
- try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(zcu), type_set);
- },
-
- .@"struct" => {
- if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
+ .@"fn" => try sema.errNote(src, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}),
+ .type => try sema.errNote(src, msg, "types are not available at runtime", .{}),
- if (zcu.typeToStruct(ty)) |struct_type| {
- for (0..struct_type.field_types.len) |i| {
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- const field_src: LazySrcLoc = .{
- .base_node_inst = struct_type.zir_index,
- .offset = .{ .container_field_type = @intCast(i) },
- };
-
- if (try field_ty.comptimeOnlySema(pt)) {
- try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{});
- try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set);
- }
- }
+ .@"struct" => if (zcu.typeToStruct(ty)) |struct_type| {
+ ty.assertHasLayout(zcu);
+ for (0..struct_type.field_types.len) |i| {
+ const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
+ if (!field_ty.comptimeOnly(zcu)) continue;
+ const field_src: LazySrcLoc = .{
+ .base_node_inst = struct_type.zir_index,
+ .offset = .{ .container_field_type = @intCast(i) },
+ };
+ try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{});
+ return sema.explainWhyTypeIsComptime(msg, field_src, field_ty);
+ }
+ unreachable;
+ } else {
+ const tuple = ip.indexToKey(ty.toIntern()).tuple_type;
+ for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty_ip, field_val_ip| {
+ if (field_val_ip != .none) continue;
+ const field_ty: Type = .fromInterned(field_ty_ip);
+ if (!field_ty.comptimeOnly(zcu)) continue;
+ try sema.errNote(src, msg, "tuple requires comptime because of field of type '{f}'", .{field_ty.fmt(pt)});
+ return sema.explainWhyTypeIsComptime(msg, src, field_ty);
}
- // TODO tuples
+ unreachable;
},
.@"union" => {
- if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
-
- if (zcu.typeToUnion(ty)) |union_obj| {
- for (0..union_obj.field_types.len) |i| {
- const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]);
- const field_src: LazySrcLoc = .{
- .base_node_inst = union_obj.zir_index,
- .offset = .{ .container_field_type = @intCast(i) },
- };
-
- if (try field_ty.comptimeOnlySema(pt)) {
- try sema.errNote(field_src, msg, "union requires comptime because of this field", .{});
- try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set);
- }
- }
+ const union_obj = zcu.typeToUnion(ty).?;
+ for (0..union_obj.field_types.len) |i| {
+ const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]);
+ if (!field_ty.comptimeOnly(zcu)) continue;
+ const field_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = .{ .container_field_type = @intCast(i) },
+ };
+ try sema.errNote(field_src, msg, "union requires comptime because of this field", .{});
+ return sema.explainWhyTypeIsComptime(msg, field_src, field_ty);
}
+ unreachable;
},
}
}
@@ -26022,9 +25306,8 @@ const ExternPosition = enum {
};
/// Returns true if `ty` is allowed in extern types.
-/// Does *NOT* require `ty` to be resolved in any way.
-/// Calls `resolveLayout` for packed containers.
-fn validateExternType(
+/// Does not require `ty` to be resolved in any way.
+pub fn validateExternType(
sema: *Sema,
ty: Type,
position: ExternPosition,
@@ -26042,7 +25325,16 @@ fn validateExternType(
.error_set,
.frame,
=> return false,
- .void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element,
+ .void => return switch (position) {
+ .ret_ty,
+ .union_field,
+ .struct_field,
+ .element,
+ => true,
+ .param_ty,
+ .other,
+ => false,
+ },
.noreturn => return position == .ret_ty,
.@"opaque",
.bool,
@@ -26050,10 +25342,12 @@ fn validateExternType(
.@"anyframe",
=> return true,
.pointer => {
- if (ty.childType(zcu).zigTypeTag(zcu) == .@"fn") {
- return ty.isConstPtr(zcu) and try sema.validateExternType(ty.childType(zcu), .other);
+ if (ty.isSlice(zcu)) return false;
+ const child_ty = ty.childType(zcu);
+ if (child_ty.zigTypeTag(zcu) == .@"fn") {
+ return ty.isConstPtr(zcu) and try sema.validateExternType(child_ty, .other);
}
- return !(ty.isSlice(zcu) or try ty.comptimeOnlySema(pt));
+ return true;
},
.int => switch (ty.intInfo(zcu).bits) {
0, 8, 16, 32, 64, 128 => return true,
@@ -26069,29 +25363,42 @@ fn validateExternType(
return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu));
},
.@"enum" => {
- return sema.validateExternType(ty.intTagType(zcu), position);
+ const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
+ if (!enum_obj.int_tag_is_explicit) return false;
+ return sema.validateExternType(.fromInterned(enum_obj.int_tag_type), position);
},
- .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
- .@"extern" => return true,
- .@"packed" => {
- const bit_size = try ty.bitSizeSema(pt);
- switch (bit_size) {
- 0, 8, 16, 32, 64, 128 => return true,
- else => return false,
- }
- },
- .auto => return !(try ty.hasRuntimeBitsSema(pt)),
+ .@"struct" => {
+ const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern());
+ return switch (struct_obj.layout) {
+ .auto => false,
+ .@"extern" => true,
+ .@"packed" => switch (struct_obj.packed_backing_mode) {
+ .auto => false,
+ .explicit => try sema.validateExternType(.fromInterned(struct_obj.packed_backing_int_type), position),
+ },
+ };
+ },
+ .@"union" => {
+ const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
+ return switch (union_obj.layout) {
+ .auto => false,
+ .@"extern" => true,
+ .@"packed" => switch (union_obj.packed_backing_mode) {
+ .auto => false,
+ .explicit => try sema.validateExternType(.fromInterned(union_obj.packed_backing_int_type), position),
+ },
+ };
},
.array => {
if (position == .ret_ty or position == .param_ty) return false;
- return sema.validateExternType(ty.elemType2(zcu), .element);
+ return sema.validateExternType(ty.childType(zcu), .element);
},
- .vector => return sema.validateExternType(ty.elemType2(zcu), .element),
+ .vector => return sema.validateExternType(ty.childType(zcu), .element),
.optional => return ty.isPtrLikeOptional(zcu),
}
}
-fn explainWhyTypeIsNotExtern(
+pub fn explainWhyTypeIsNotExtern(
sema: *Sema,
msg: *Zcu.ErrorMsg,
src_loc: LazySrcLoc,
@@ -26125,9 +25432,6 @@ fn explainWhyTypeIsNotExtern(
const pointee_ty = ty.childType(zcu);
if (!ty.isConstPtr(zcu) and pointee_ty.zigTypeTag(zcu) == .@"fn") {
try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{});
- } else if (try ty.comptimeOnlySema(pt)) {
- try sema.errNote(src_loc, msg, "pointer to comptime-only type '{f}'", .{pointee_ty.fmt(pt)});
- try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
}
try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other);
}
@@ -26157,6 +25461,7 @@ fn explainWhyTypeIsNotExtern(
try sema.errNote(src_loc, msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)});
try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
},
+ // MLUGG TODO: these notes are bad now (because ABI sized packed type also needs explicit backing type)
.@"struct" => try sema.errNote(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
.@"union" => try sema.errNote(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
.array => {
@@ -26165,51 +25470,14 @@ fn explainWhyTypeIsNotExtern(
} else if (position == .param_ty) {
return sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{});
}
- try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element);
+ try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .element);
},
- .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element),
+ .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .element),
.optional => try sema.errNote(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
}
}
-/// Returns true if `ty` is allowed in packed types.
-/// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only.
-fn validatePackedType(sema: *Sema, ty: Type) !bool {
- const pt = sema.pt;
- const zcu = pt.zcu;
- return switch (ty.zigTypeTag(zcu)) {
- .type,
- .comptime_float,
- .comptime_int,
- .enum_literal,
- .undefined,
- .null,
- .error_union,
- .error_set,
- .frame,
- .noreturn,
- .@"opaque",
- .@"anyframe",
- .@"fn",
- .array,
- => false,
- .optional => return ty.isPtrLikeOptional(zcu),
- .void,
- .bool,
- .float,
- .int,
- .vector,
- => true,
- .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).tag_mode) {
- .auto => false,
- .explicit, .nonexhaustive => true,
- },
- .pointer => !ty.isSlice(zcu) and !try ty.comptimeOnlySema(pt),
- .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
- };
-}
-
-fn explainWhyTypeIsNotPacked(
+pub fn explainWhyTypeIsNotPackable(
sema: *Sema,
msg: *Zcu.ErrorMsg,
src_loc: LazySrcLoc,
@@ -26250,8 +25518,8 @@ fn explainWhyTypeIsNotPacked(
try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{});
try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{});
},
- .@"struct" => try sema.errNote(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
- .@"union" => try sema.errNote(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
+ .@"struct" => try sema.errNote(src_loc, msg, "struct in packed type must have packed layout", .{}),
+ .@"union" => try sema.errNote(src_loc, msg, "union in packed type must have packed layout", .{}),
}
}
@@ -26277,7 +25545,7 @@ fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !In
try sema.ensureMemoizedStateResolved(src, .panic);
const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin());
switch (sema.owner.unwrap()) {
- .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
+ .@"comptime", .nav_ty, .nav_val, .type_layout, .type_inits, .memoized_state => {},
.func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true),
}
return panic_fn_index;
@@ -26555,7 +25823,8 @@ fn fieldPtrLoad(
const zcu = pt.zcu;
const object_ptr_ty = sema.typeOf(object_ptr);
const pointee_ty = object_ptr_ty.childType(zcu);
- if (try typeHasOnePossibleValue(sema, pointee_ty)) |opv| {
+ try sema.ensureLayoutResolved(pointee_ty); // MLUGG TODO
+ if (try pointee_ty.onePossibleValue(pt)) |opv| {
const object: Air.Inst.Ref = .fromValue(opv);
return fieldVal(sema, block, src, object, field_name, field_name_src);
}
@@ -26603,7 +25872,7 @@ fn fieldVal(
return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern());
} else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
const ptr_info = object_ty.ptrInfo(zcu);
- const result_ty = try pt.ptrTypeSema(.{
+ const result_ty = try pt.ptrType(.{
.child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
.sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
.flags = .{
@@ -26693,7 +25962,6 @@ fn fieldVal(
if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
return inst;
}
- try child_type.resolveFields(pt);
if (child_type.unionTagType(zcu)) |enum_ty| {
if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| {
const field_index: u32 = @intCast(field_index_usize);
@@ -26731,6 +25999,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);
const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
return sema.analyzeLoad(block, src, field_ptr, object_src);
} else {
@@ -26738,6 +26007,7 @@ fn fieldVal(
},
.@"union" => if (is_pointer_to) {
// Avoid loading the entire union by fetching a pointer and loading that
+ try sema.ensureLayoutResolved(inner_ty);
const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
return sema.analyzeLoad(block, src, field_ptr, object_src);
} else {
@@ -26787,7 +26057,7 @@ fn fieldPtr(
return uavRef(sema, int_val.toIntern());
} else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
const ptr_info = object_ty.ptrInfo(zcu);
- const new_ptr_ty = try pt.ptrTypeSema(.{
+ const new_ptr_ty = try pt.ptrType(.{
.child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
.sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
.flags = .{
@@ -26802,7 +26072,7 @@ fn fieldPtr(
.packed_offset = ptr_info.packed_offset,
});
const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu);
- const result_ty = try pt.ptrTypeSema(.{
+ const result_ty = try pt.ptrType(.{
.child = new_ptr_ty.toIntern(),
.sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
.flags = .{
@@ -26836,7 +26106,7 @@ fn fieldPtr(
if (field_name.eqlSlice("ptr", ip)) {
const slice_ptr_ty = inner_ty.slicePtrFieldType(zcu);
- const result_ty = try pt.ptrTypeSema(.{
+ const result_ty = try pt.ptrType(.{
.child = slice_ptr_ty.toIntern(),
.flags = .{
.is_const = !attr_ptr_ty.ptrIsMutable(zcu),
@@ -26854,7 +26124,7 @@ fn fieldPtr(
try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
return field_ptr;
} else if (field_name.eqlSlice("len", ip)) {
- const result_ty = try pt.ptrTypeSema(.{
+ const result_ty = try pt.ptrType(.{
.child = .usize_type,
.flags = .{
.is_const = !attr_ptr_ty.ptrIsMutable(zcu),
@@ -26925,7 +26195,6 @@ fn fieldPtr(
if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
return inst;
}
- try child_type.resolveFields(pt);
if (child_type.unionTagType(zcu)) |enum_ty| {
if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
const field_index_u32: u32 = @intCast(field_index);
@@ -26960,6 +26229,7 @@ fn fieldPtr(
try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
else
object_ptr;
+ try sema.ensureLayoutResolved(inner_ty);
const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
return field_ptr;
@@ -26969,6 +26239,7 @@ fn fieldPtr(
try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
else
object_ptr;
+ try sema.ensureLayoutResolved(inner_ty);
const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
return field_ptr;
@@ -27012,6 +26283,7 @@ fn fieldCallBind(
// Optionally dereference a second pointer to get the concrete type.
const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one;
const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty;
+ try sema.ensureLayoutResolved(concrete_ty);
const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
const object_ptr = if (is_double_ptr)
try sema.analyzeLoad(block, src, raw_ptr, src)
@@ -27021,10 +26293,8 @@ fn fieldCallBind(
find_field: {
switch (concrete_ty.zigTypeTag(zcu)) {
.@"struct" => {
- try concrete_ty.resolveFields(pt);
if (zcu.typeToStruct(concrete_ty)) |struct_type| {
- const field_index = struct_type.nameIndex(ip, field_name) orelse
- break :find_field;
+ const field_index = struct_type.nameIndex(ip, field_name) orelse break :find_field;
const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr);
@@ -27047,9 +26317,9 @@ fn fieldCallBind(
}
},
.@"union" => {
- try concrete_ty.resolveFields(pt);
const union_obj = zcu.typeToUnion(concrete_ty).?;
- _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field;
+ const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
+ if (enum_obj.nameIndex(ip, field_name) == null) break :find_field;
const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false);
return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) };
},
@@ -27163,7 +26433,7 @@ fn finishFieldCallBind(
) CompileError!ResolvedFieldCallee {
const pt = sema.pt;
const zcu = pt.zcu;
- const ptr_field_ty = try pt.ptrTypeSema(.{
+ const ptr_field_ty = try pt.ptrType(.{
.child = field_ty.toIntern(),
.flags = .{
.is_const = !ptr_ty.ptrIsMutable(zcu),
@@ -27174,7 +26444,9 @@ fn finishFieldCallBind(
const container_ty = ptr_ty.childType(zcu);
if (container_ty.zigTypeTag(zcu) == .@"struct") {
if (container_ty.structFieldIsComptime(field_index, zcu)) {
- try container_ty.resolveStructFieldInits(pt);
+ if (!container_ty.isTuple(zcu)) {
+ try sema.ensureFieldInitsResolved(container_ty);
+ }
const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?;
return .{ .direct = Air.internedToRef(default_val.toIntern()) };
}
@@ -27239,6 +26511,7 @@ fn namespaceLookupVal(
return try sema.analyzeNavVal(block, src, nav);
}
+/// Asserts that the layout of `struct_ty` is already resolved.
fn structFieldPtr(
sema: *Sema,
block: *Block,
@@ -27252,10 +26525,9 @@ fn structFieldPtr(
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- assert(struct_ty.zigTypeTag(zcu) == .@"struct");
- try struct_ty.resolveFields(pt);
- try struct_ty.resolveLayout(pt);
+ assert(struct_ty.zigTypeTag(zcu) == .@"struct");
+ struct_ty.assertHasLayout(zcu);
if (struct_ty.isTuple(zcu)) {
if (field_name.eqlSlice("len", ip)) {
@@ -27274,6 +26546,7 @@ fn structFieldPtr(
return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty);
}
+/// Asserts that the layout of `struct_ty` is already resolved.
fn structFieldPtrByIndex(
sema: *Sema,
block: *Block,
@@ -27286,8 +26559,10 @@ fn structFieldPtrByIndex(
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.fieldIsComptime(ip, field_index);
+ const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index);
// Comptime fields are handled later
if (!field_is_comptime) {
@@ -27300,6 +26575,7 @@ fn structFieldPtrByIndex(
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,
@@ -27313,7 +26589,7 @@ fn structFieldPtrByIndex(
const parent_align = if (struct_ptr_ty_info.flags.alignment != .none)
struct_ptr_ty_info.flags.alignment
else
- try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt);
+ struct_ty.abiAlignment(zcu);
if (struct_type.layout == .@"packed") {
assert(!field_is_comptime);
@@ -27325,31 +26601,32 @@ fn structFieldPtrByIndex(
// 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.
- const field_offset = struct_ty.structFieldOffset(field_index, zcu);
- ptr_ty_data.flags.alignment = if (parent_align == .none)
- .none
- else
- @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset)));
+ 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.
- const field_align = try Type.fromInterned(field_ty).structFieldAlignmentSema(
- struct_type.fieldAlign(ip, field_index),
- struct_type.layout,
- pt,
- );
ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
- field_align
+ struct_ty.explicitFieldAlignment(field_index, zcu)
else
- field_align.min(parent_align);
+ struct_ty.resolvedFieldAlignment(field_index, zcu).min(parent_align);
}
- const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data);
+ const ptr_field_ty = try pt.ptrType(ptr_ty_data);
if (field_is_comptime) {
- try struct_ty.resolveStructFieldInits(pt);
+ try sema.ensureFieldInitsResolved(struct_ty);
const val = try pt.intern(.{ .ptr = .{
.ty = ptr_field_ty.toIntern(),
- .base_addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
+ .base_addr = .{ .comptime_field = struct_type.field_defaults.get(ip)[field_index] },
.byte_offset = 0,
} });
return Air.internedToRef(val);
@@ -27371,32 +26648,26 @@ fn structFieldVal(
const ip = &zcu.intern_pool;
assert(struct_ty.zigTypeTag(zcu) == .@"struct");
- try struct_ty.resolveFields(pt);
-
switch (ip.indexToKey(struct_ty.toIntern())) {
.struct_type => {
const struct_type = ip.loadStructType(struct_ty.toIntern());
const field_index = struct_type.nameIndex(ip, field_name) orelse
return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
- if (struct_type.fieldIsComptime(ip, field_index)) {
- try struct_ty.resolveStructFieldInits(pt);
- return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
+ if (struct_type.field_is_comptime_bits.get(ip, field_index)) {
+ try sema.ensureFieldInitsResolved(struct_ty);
+ return .fromIntern(struct_type.field_defaults.get(ip)[field_index]);
}
const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
- if (try sema.typeHasOnePossibleValue(field_ty)) |field_val|
- return Air.internedToRef(field_val.toIntern());
+ if (try field_ty.onePossibleValue(pt)) |field_val|
+ return .fromValue(field_val);
if (try sema.resolveValue(struct_byval)) |struct_val| {
if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty);
- if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
- return Air.internedToRef(opv.toIntern());
- }
- return Air.internedToRef((try struct_val.fieldValue(pt, field_index)).toIntern());
+ return .fromValue(try struct_val.fieldValue(pt, field_index));
}
- try field_ty.resolveLayout(pt);
return block.addStructFieldVal(struct_byval, field_index, field_ty);
},
.tuple_type => {
@@ -27457,16 +26728,13 @@ fn tupleFieldValByIndex(
const zcu = pt.zcu;
const field_ty = tuple_ty.fieldType(field_index, zcu);
- if (tuple_ty.structFieldIsComptime(field_index, zcu))
- try tuple_ty.resolveStructFieldInits(pt);
if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
return Air.internedToRef(default_value.toIntern());
}
+ if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
+
if (try sema.resolveValue(tuple_byval)) |tuple_val| {
- if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
- return Air.internedToRef(opv.toIntern());
- }
return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) {
.undef => pt.undefRef(field_ty),
.aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) {
@@ -27478,10 +26746,10 @@ fn tupleFieldValByIndex(
};
}
- try field_ty.resolveLayout(pt);
return block.addStructFieldVal(tuple_byval, field_index, field_ty);
}
+/// Asserts that the layout of `union_ty` is already resolved.
fn unionFieldPtr(
sema: *Sema,
block: *Block,
@@ -27497,31 +26765,31 @@ fn unionFieldPtr(
const ip = &zcu.intern_pool;
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);
- try union_ty.resolveFields(pt);
const union_obj = zcu.typeToUnion(union_ty).?;
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.ptrTypeSema(.{
+ 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 = if (union_obj.flagsUnordered(ip).layout == .auto) blk: {
- const union_align = if (union_ptr_info.flags.alignment != .none)
- union_ptr_info.flags.alignment
- else
- try union_ty.abiAlignmentSema(pt);
- const field_align = try union_ty.fieldAlignmentSema(field_index, pt);
- break :blk union_align.min(field_align);
- } else union_ptr_info.flags.alignment,
+ .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_ty).enumFieldIndex(field_name, zcu).?);
+ const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_type).enumFieldIndex(field_name, zcu).?);
if (initializing and field_ty.zigTypeTag(zcu) == .noreturn) {
const msg = msg: {
@@ -27538,16 +26806,16 @@ fn unionFieldPtr(
}
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
- switch (union_obj.flagsUnordered(ip).layout) {
+ switch (union_obj.layout) {
.auto => if (initializing) {
if (!sema.isComptimeMutablePtr(union_ptr_val)) {
// The initialization is a runtime operation.
break :ct;
}
// Store to the union to initialize the tag.
- const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
+ 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 pt.undefValue(payload_ty));
+ const new_union_val = try pt.unionValue(union_ty, field_tag, try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty));
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
@@ -27556,12 +26824,12 @@ fn unionFieldPtr(
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_ty), enum_field_index);
+ 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 msg = msg: {
- const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
- const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu);
+ 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 msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
field_name.fmt(ip),
active_field_name.fmt(ip),
@@ -27582,15 +26850,15 @@ 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_ty);
- if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag;
+ const tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
+ if (try tag_ty.onePossibleValue(pt) != null) 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);
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
- } else if (block.wantSafety() and union_obj.hasTag(ip)) {
+ } else if (block.wantSafety() and union_obj.runtime_tag != .none) {
// The tag exists at runtime (safety tag), so emit a safety check.
// TODO would it be better if get_union_tag supported pointers to unions?
const union_val = try block.addTyOp(.load, union_ty, union_ptr);
@@ -27619,26 +26887,25 @@ fn unionFieldVal(
const ip = &zcu.intern_pool;
assert(union_ty.zigTypeTag(zcu) == .@"union");
- try union_ty.resolveFields(pt);
const union_obj = zcu.typeToUnion(union_ty).?;
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 enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?);
+ const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_type).enumFieldIndex(field_name, zcu).?);
if (try sema.resolveValue(union_byval)) |union_val| {
if (union_val.isUndef(zcu)) return pt.undefRef(field_ty);
const un = ip.indexToKey(union_val.toIntern()).un;
- const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
+ const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), enum_field_index);
const tag_matches = un.tag == field_tag.toIntern();
- switch (union_obj.flagsUnordered(ip).layout) {
+ switch (union_obj.layout) {
.auto => {
if (tag_matches) {
return Air.internedToRef(un.val);
} else {
const msg = msg: {
- const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
- const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu);
+ 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 msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
field_name.fmt(ip), active_field_name.fmt(ip),
});
@@ -27658,18 +26925,18 @@ fn unionFieldVal(
.@"packed" => if (tag_matches) {
// Fast path - no need to use bitcast logic.
return Air.internedToRef(un.val);
- } else if (try sema.bitCastVal(union_val, field_ty, 0, try union_ty.bitSizeSema(pt), 0)) |field_val| {
+ } else if (try sema.bitCastVal(union_val, field_ty, 0, union_ty.bitSize(zcu), 0)) |field_val| {
return Air.internedToRef(field_val.toIntern());
},
}
}
- if (union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and
+ if (union_obj.layout == .auto and block.wantSafety() and
union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1)
{
- const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
+ const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), enum_field_index);
const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
- const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_byval);
+ const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_type), union_byval);
try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag);
}
@@ -27678,11 +26945,8 @@ fn unionFieldVal(
return .unreachable_value;
}
- if (try sema.typeHasOnePossibleValue(field_ty)) |field_only_value| {
- return Air.internedToRef(field_only_value.toIntern());
- }
+ if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
- try field_ty.resolveLayout(pt);
return block.addStructFieldVal(union_byval, field_index, field_ty);
}
@@ -27706,17 +26970,19 @@ fn elemPtr(
else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}),
};
try sema.checkIndexable(block, src, indexable_ty);
+ try sema.ensureLayoutResolved(indexable_ty);
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(try index_val.toUnsignedIntSema(pt));
+ const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
},
else => {
const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
+ try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu));
return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
},
};
@@ -27725,7 +26991,7 @@ fn elemPtr(
return elem_ptr;
}
-/// Asserts that the type of indexable is pointer.
+/// Asserts that `indexable` is an indexable pointer whose child type has its layout already resolved.
fn elemPtrOneLayerOnly(
sema: *Sema,
block: *Block,
@@ -27741,7 +27007,10 @@ fn elemPtrOneLayerOnly(
const pt = sema.pt;
const zcu = pt.zcu;
- try sema.checkIndexable(block, src, indexable_ty);
+ assert(indexable_ty.isIndexable(zcu));
+ assert(indexable_ty.zigTypeTag(zcu) == .pointer);
+ const child_ty = indexable_ty.childType(zcu);
+ child_ty.assertHasLayout(zcu);
switch (indexable_ty.ptrSize(zcu)) {
.slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
@@ -27751,7 +27020,7 @@ fn elemPtrOneLayerOnly(
ct: {
const ptr_val = maybe_ptr_val orelse break :ct;
const index_val = maybe_index_val orelse break :ct;
- const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
+ const index: usize = @intCast(index_val.toUnsignedInt(zcu));
const elem_ptr = try ptr_val.ptrElem(index, pt);
return Air.internedToRef(elem_ptr.toIntern());
}
@@ -27762,7 +27031,7 @@ fn elemPtrOneLayerOnly(
try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src);
try sema.validateRuntimeValue(block, indexable_src, indexable);
- if (!try result_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) {
+ if (result_ty.childType(zcu).abiSize(zcu) == 0) {
// zero-bit child type; just bitcast the pointer
return block.addBitCast(result_ty, indexable);
}
@@ -27770,13 +27039,12 @@ fn elemPtrOneLayerOnly(
return block.addPtrElemPtr(indexable, elem_index, result_ty);
},
.one => {
- const child_ty = indexable_ty.childType(zcu);
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(try index_val.toUnsignedIntSema(pt));
+ const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
},
else => unreachable, // Guaranteed by checkIndexable
@@ -27808,45 +27076,45 @@ fn elemVal(
const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src);
switch (indexable_ty.zigTypeTag(zcu)) {
- .pointer => switch (indexable_ty.ptrSize(zcu)) {
- .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
- .many, .c => {
- const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- const elem_ty = indexable_ty.elemType2(zcu);
+ .pointer => {
+ const child_ty = indexable_ty.childType(zcu);
+ try sema.ensureLayoutResolved(child_ty);
+ switch (indexable_ty.ptrSize(zcu)) {
+ .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
+ .many, .c => {
+ const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- ct: {
- const indexable_val = maybe_indexable_val orelse break :ct;
- const index_val = maybe_index_val orelse break :ct;
- const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
- const many_ptr_ty = try pt.manyConstPtrType(elem_ty);
- const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty);
- const elem_ptr_ty = try pt.singleConstPtrType(elem_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, elem_ty)).toIntern());
- }
+ ct: {
+ const indexable_val = maybe_indexable_val orelse break :ct;
+ const index_val = maybe_index_val orelse break :ct;
+ 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());
+ }
- if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| {
- return Air.internedToRef(elem_only_value.toIntern());
- }
+ if (try child_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
- try sema.checkLogicalPtrOperation(block, src, indexable_ty);
- return block.addBinOp(.ptr_elem_val, indexable, elem_index);
- },
- .one => {
- arr_sent: {
- const inner_ty = indexable_ty.childType(zcu);
- if (inner_ty.zigTypeTag(zcu) != .array) break :arr_sent;
- const sentinel = inner_ty.sentinel(zcu) orelse break :arr_sent;
- const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
- const index = try sema.usizeCast(block, src, try index_val.toUnsignedIntSema(pt));
- if (index != inner_ty.arrayLen(zcu)) break :arr_sent;
- return Air.internedToRef(sentinel.toIntern());
- }
- const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
- return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
- },
+ try sema.checkLogicalPtrOperation(block, src, indexable_ty);
+ return block.addBinOp(.ptr_elem_val, indexable, elem_index);
+ },
+ .one => {
+ arr_sent: {
+ if (child_ty.zigTypeTag(zcu) != .array) break :arr_sent;
+ const sentinel = child_ty.sentinel(zcu) orelse break :arr_sent;
+ const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
+ const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(zcu));
+ if (index != child_ty.arrayLen(zcu)) break :arr_sent;
+ return .fromValue(sentinel);
+ }
+ const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
+ return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
+ },
+ }
},
.array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
.vector => {
@@ -27856,7 +27124,7 @@ fn elemVal(
.@"struct" => {
// Tuple field access.
const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
- const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
+ const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
},
else => unreachable,
@@ -27864,6 +27132,7 @@ fn elemVal(
}
/// Called when the index or indexable is runtime known.
+/// Asserts that the layout of `elem_ty` is already resolved.
fn validateRuntimeElemAccess(
sema: *Sema,
block: *Block,
@@ -27875,7 +27144,7 @@ fn validateRuntimeElemAccess(
const pt = sema.pt;
const zcu = pt.zcu;
- if (try elem_ty.comptimeOnlySema(sema.pt)) {
+ if (elem_ty.comptimeOnly(zcu)) {
const msg = msg: {
const msg = try sema.errMsg(
elem_index_src,
@@ -27900,6 +27169,7 @@ fn validateRuntimeElemAccess(
}
}
+/// Asserts that the layout of the tuple type is already resolved.
fn tupleFieldPtr(
sema: *Sema,
block: *Block,
@@ -27914,9 +27184,10 @@ fn tupleFieldPtr(
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);
- try tuple_ty.resolveFields(pt);
const field_count = tuple_ty.structFieldCount(zcu);
+ tuple_ty.assertHasLayout(zcu);
+
if (field_count == 0) {
return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
}
@@ -27928,7 +27199,7 @@ fn tupleFieldPtr(
}
const field_ty = tuple_ty.fieldType(field_index, zcu);
- const ptr_field_ty = try pt.ptrTypeSema(.{
+ const ptr_field_ty = try pt.ptrType(.{
.child = field_ty.toIntern(),
.flags = .{
.is_const = tuple_ptr_info.flags.is_const,
@@ -27938,15 +27209,12 @@ fn tupleFieldPtr(
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 = try field_ty.abiAlignmentSema(pt);
+ const field_align = field_ty.abiAlignment(zcu);
break :a tuple_align.min(field_align);
},
},
});
- if (tuple_ty.structFieldIsComptime(field_index, zcu))
- try tuple_ty.resolveStructFieldInits(pt);
-
if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| {
return Air.internedToRef((try pt.intern(.{ .ptr = .{
.ty = ptr_field_ty.toIntern(),
@@ -27978,7 +27246,6 @@ fn tupleField(
const pt = sema.pt;
const zcu = pt.zcu;
const tuple_ty = sema.typeOf(tuple);
- try tuple_ty.resolveFields(pt);
const field_count = tuple_ty.structFieldCount(zcu);
if (field_count == 0) {
@@ -27993,8 +27260,6 @@ fn tupleField(
const field_ty = tuple_ty.fieldType(field_index, zcu);
- if (tuple_ty.structFieldIsComptime(field_index, zcu))
- try tuple_ty.resolveStructFieldInits(pt);
if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
return Air.internedToRef(default_value.toIntern()); // comptime field
}
@@ -28006,7 +27271,6 @@ fn tupleField(
try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
- try field_ty.resolveLayout(pt);
return block.addStructFieldVal(tuple, field_index, field_ty);
}
@@ -28037,7 +27301,7 @@ fn elemValArray(
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
if (maybe_index_val) |index_val| {
- const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
+ const index: usize = @intCast(index_val.toUnsignedInt(zcu));
if (array_sent) |s| {
if (index == array_len) {
return Air.internedToRef(s.toIntern());
@@ -28053,10 +27317,11 @@ fn elemValArray(
return pt.undefRef(elem_ty);
}
if (maybe_index_val) |index_val| {
- const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
- const elem_val = try array_val.elemValue(pt, index);
- return Air.internedToRef(elem_val.toIntern());
+ const index: usize = @intCast(index_val.toUnsignedInt(zcu));
+ return .fromValue(try array_val.elemValue(pt, index));
}
+ // Since the array is comptime-known, it might be OPV, in which case the index is irrelevant.
+ if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
}
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
@@ -28071,12 +27336,10 @@ fn elemValArray(
}
}
- if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_val|
- return Air.internedToRef(elem_val.toIntern());
-
return block.addBinOp(.array_elem_val, array, elem_index);
}
+/// Asserts that the layout of the array or vector is already resolved.
fn elemPtrArray(
sema: *Sema,
block: *Block,
@@ -28103,7 +27366,7 @@ fn elemPtrArray(
const maybe_undef_array_ptr_val = try 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, try index_val.toUnsignedIntSema(pt));
+ const index = try sema.usizeCast(block, elem_index_src, 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 });
@@ -28115,6 +27378,7 @@ fn elemPtrArray(
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);
if (maybe_undef_array_ptr_val) |array_ptr_val| {
@@ -28128,7 +27392,7 @@ fn elemPtrArray(
}
if (!init) {
- try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src);
+ try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.childType(zcu), array_ty, array_ptr_src);
try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);
}
@@ -28142,6 +27406,7 @@ fn elemPtrArray(
return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
}
+/// Asserts that the layout of the slice element type is already resolved.
fn elemValSlice(
sema: *Sema,
block: *Block,
@@ -28156,9 +27421,11 @@ fn elemValSlice(
const zcu = pt.zcu;
const slice_ty = sema.typeOf(slice);
const slice_sent = slice_ty.sentinel(zcu) != null;
- const elem_ty = slice_ty.elemType2(zcu);
+ const elem_ty = slice_ty.childType(zcu);
var runtime_src = slice_src;
+ elem_ty.assertHasLayout(zcu);
+
// slice must be defined since it can dereferenced as null
const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
// index must be defined since it can index out of bounds
@@ -28166,13 +27433,13 @@ fn elemValSlice(
if (maybe_slice_val) |slice_val| {
runtime_src = elem_index_src;
- const slice_len = try slice_val.sliceLen(pt);
+ 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", .{});
}
if (maybe_index_val) |index_val| {
- const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
+ const index: usize = @intCast(index_val.toUnsignedInt(zcu));
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 });
@@ -28186,16 +27453,14 @@ fn elemValSlice(
}
}
- if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| {
- return Air.internedToRef(elem_only_value.toIntern());
- }
+ if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
try sema.validateRuntimeValue(block, slice_src, slice);
if (oob_safety and block.wantSafety()) {
const len_inst = if (maybe_slice_val) |slice_val|
- try pt.intRef(.usize, try slice_val.sliceLen(pt))
+ try pt.intRef(.usize, slice_val.sliceLen(zcu))
else
try block.addTyOp(.slice_len, .usize, slice);
const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
@@ -28204,6 +27469,7 @@ fn elemValSlice(
return block.addBinOp(.slice_elem_val, slice, elem_index);
}
+/// Asserts that the layout of the slice element type is already resolved.
fn elemPtrSlice(
sema: *Sema,
block: *Block,
@@ -28219,11 +27485,12 @@ fn elemPtrSlice(
const slice_ty = sema.typeOf(slice);
const slice_sent = slice_ty.sentinel(zcu) != null;
+ slice_ty.childType(zcu).assertHasLayout(zcu);
+
const maybe_undef_slice_val = try 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: {
- const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt));
- break :o index;
+ break :o try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(zcu));
} else null;
const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt);
@@ -28232,7 +27499,7 @@ fn elemPtrSlice(
if (slice_val.isUndef(zcu)) {
return pt.undefRef(elem_ptr_ty);
}
- const slice_len = try slice_val.sliceLen(pt);
+ 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", .{});
@@ -28254,13 +27521,13 @@ fn elemPtrSlice(
const len_inst = len: {
if (maybe_undef_slice_val) |slice_val|
if (!slice_val.isUndef(zcu))
- break :len try pt.intRef(.usize, try slice_val.sliceLen(pt));
+ break :len try pt.intRef(.usize, slice_val.sliceLen(zcu));
break :len try block.addTyOp(.slice_len, .usize, slice);
};
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 (!try slice_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) {
+ if (slice_ty.childType(zcu).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);
@@ -28331,10 +27598,12 @@ fn coerceExtra(
if (dest_ty.isGenericPoison()) return inst;
const dest_ty_src = inst_src; // TODO better source location
- try dest_ty.resolveFields(pt);
const inst_ty = sema.typeOf(inst);
- try inst_ty.resolveFields(pt);
const target = zcu.getTarget();
+
+ inst_ty.assertHasLayout(zcu);
+ try sema.ensureLayoutResolved(dest_ty);
+
// If the types are the same, we can return the operand.
if (dest_ty.eql(inst_ty, zcu))
return inst;
@@ -28357,7 +27626,7 @@ fn coerceExtra(
if (maybe_inst_val) |val| {
// undefined sets the optional bit also to undefined.
if (val.toIntern() == .undef) {
- return pt.undefRef(dest_ty);
+ return .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty));
}
// null to ?T
@@ -28372,11 +27641,11 @@ fn coerceExtra(
// cast from ?*T and ?[*]T to ?*anyopaque
// but don't do it if the source type is a double pointer
if (dest_ty.isPtrLikeOptional(zcu) and
- dest_ty.elemType2(zcu).toIntern() == .anyopaque_type and
+ dest_ty.nullablePtrElem(zcu).toIntern() == .anyopaque_type and
inst_ty.isPtrAtRuntime(zcu))
anyopaque_check: {
if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
- const elem_ty = inst_ty.elemType2(zcu);
+ const elem_ty = inst_ty.nullablePtrElem(zcu);
if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
in_memory_result = .{ .double_ptr_to_anyopaque = .{
.actual = inst_ty,
@@ -28520,7 +27789,7 @@ fn coerceExtra(
// but don't do it if the source type is a double pointer
if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: {
if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
- const elem_ty = inst_ty.elemType2(zcu);
+ const elem_ty = inst_ty.childType(zcu);
if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
in_memory_result = .{ .double_ptr_to_anyopaque = .{
.actual = inst_ty,
@@ -28616,7 +27885,9 @@ fn coerceExtra(
// empty tuple to zero-length slice
// note that this allows coercing to a mutable slice.
if (inst_child_ty.structFieldCount(zcu) == 0) {
- const align_val = try dest_ty.ptrAlignmentSema(pt);
+ // TODO MLUGG: this is *unacceptably* stupid. we're resolving the child for the alignment value
+ try sema.ensureLayoutResolved(dest_ty.childType(zcu));
+ const align_val = dest_ty.ptrAlignment(zcu);
return Air.internedToRef(try pt.intern(.{ .slice = .{
.ty = dest_ty.toIntern(),
.ptr = try pt.intern(.{ .ptr = .{
@@ -28689,7 +27960,7 @@ fn coerceExtra(
return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) });
}
return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
- .undef => try pt.undefRef(dest_ty),
+ .undef => .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty)),
.int => |int| Air.internedToRef(
try zcu.intern_pool.getCoercedInts(gpa, io, pt.tid, int, dest_ty.toIntern()),
),
@@ -28768,7 +28039,7 @@ fn coerceExtra(
}
break :int;
};
- const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
+ const result_val = try pt.floatValue(dest_ty, val.toFloat(f128, zcu));
const fits: bool = switch (ip.indexToKey(result_val.toIntern())) {
else => unreachable,
.undef => true,
@@ -28905,11 +28176,11 @@ fn coerceExtra(
else => true,
};
- if (can_coerce_to) {
+ if (can_coerce_to and inst == .undef) {
// undefined to anything. We do this after the big switch above so that
// special logic has a chance to run first, such as `*[N]T` to `[]T` which
// should initialize the length field of the slice.
- if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty);
+ return .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty));
}
if (!opts.report_err) return error.NotCoercible;
@@ -29444,17 +28715,13 @@ pub fn coerceInMemoryAllowed(
}
// Pointers / Pointer-like Optionals
- const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty);
- const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
- if (maybe_dest_ptr_ty) |dest_ptr_ty| {
- if (maybe_src_ptr_ty) |src_ptr_ty| {
- return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
- }
+ if (dest_ty.isPtrAtRuntime(zcu) and src_ty.isPtrAtRuntime(zcu)) {
+ return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
}
// Slices
if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) {
- return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
+ return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
}
// Functions
@@ -29554,7 +28821,8 @@ pub fn coerceInMemoryAllowed(
// Optionals
if (dest_tag == .optional and src_tag == .optional) {
- if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
+ if (dest_ty.isPtrAtRuntime(zcu) or src_ty.isPtrAtRuntime(zcu)) {
+ // Only one is, because we already handled when both are.
return .{ .optional_shape = .{
.actual = src_ty,
.wanted = dest_ty,
@@ -29581,7 +28849,7 @@ 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.fieldAlignment(field_idx, zcu) != src_ty.fieldAlignment(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);
@@ -29714,11 +28982,7 @@ fn coerceInMemoryAllowedFns(
{
if (dest_info.is_var_args != src_info.is_var_args) {
- return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
- }
-
- if (dest_info.is_generic != src_info.is_generic) {
- return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
+ return .{ .fn_var_args = dest_info.is_var_args };
}
const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and
@@ -29731,6 +28995,12 @@ fn coerceInMemoryAllowedFns(
} };
}
+ try sema.ensureLayoutResolved(src_ty);
+ try sema.ensureLayoutResolved(dest_ty);
+ const src_is_runtime = src_ty.fnHasRuntimeBits(zcu);
+ const dest_is_runtime = dest_ty.fnHasRuntimeBits(zcu);
+ if (src_is_runtime != dest_is_runtime) return .{ .fn_generic = !dest_is_runtime };
+
if (!switch (src_info.return_type) {
.generic_poison_type => true,
.noreturn_type => !dest_is_mut,
@@ -29780,7 +29050,8 @@ fn coerceInMemoryAllowedFns(
const src_is_comptime = src_info.paramIsComptime(@intCast(param_i));
const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i));
if (src_is_comptime == dest_is_comptime) break :comptime_param;
- if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) {
+ try sema.ensureLayoutResolved(dest_param_ty);
+ if (!dest_is_mut and src_is_comptime and !dest_is_comptime and dest_param_ty.comptimeOnly(zcu)) {
// A parameter which is marked `comptime` can drop that annotation if the type is comptime-only.
// The function remains generic, and the parameter is going to be comptime-resolved either way,
// so this just affects whether or not the argument is comptime-evaluated at the call site.
@@ -29861,8 +29132,6 @@ fn coerceInMemoryAllowedPtrs(
block: *Block,
dest_ty: Type,
src_ty: Type,
- dest_ptr_ty: Type,
- src_ptr_ty: Type,
/// If set, the coercion must be valid in both directions.
dest_is_mut: bool,
target: *const std.Target,
@@ -29875,8 +29144,8 @@ fn coerceInMemoryAllowedPtrs(
const gpa = comp.gpa;
const io = comp.io;
- const dest_info = dest_ptr_ty.ptrInfo(zcu);
- const src_info = src_ptr_ty.ptrInfo(zcu);
+ const dest_info = dest_ty.ptrInfo(zcu);
+ const src_info = src_ty.ptrInfo(zcu);
const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
src_info.flags.size == .c or dest_info.flags.size == .c;
@@ -30008,16 +29277,14 @@ fn coerceInMemoryAllowedPtrs(
if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
dest_info.child != src_info.child)
{
- const src_align = if (src_info.flags.alignment != .none)
- src_info.flags.alignment
- else
- try Type.fromInterned(src_info.child).abiAlignmentSema(pt);
-
- const dest_align = if (dest_info.flags.alignment != .none)
- dest_info.flags.alignment
- else
- try Type.fromInterned(dest_info.child).abiAlignmentSema(pt);
-
+ const src_align = if (src_info.flags.alignment == .none) a: {
+ try sema.ensureLayoutResolved(src_child);
+ break :a src_child.abiAlignment(zcu);
+ } else src_info.flags.alignment;
+ const dest_align = if (dest_info.flags.alignment == .none) a: {
+ try sema.ensureLayoutResolved(dest_child);
+ break :a dest_child.abiAlignment(zcu);
+ } else dest_info.flags.alignment;
if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) {
return InMemoryCoercionResult{ .ptr_alignment = .{
.actual = src_align,
@@ -30180,9 +29447,16 @@ fn storePtr2(
return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
};
+ // We do this after the possible comptime store above, for the case of field_ptr stores
+ // to unions because we want the comptime tag to be set, even if the field type is void.
+ // MLUGG TODO: that's insane, the runtime and comptime sematics should be the same. just set the tag at the same damn time
+ if (try elem_ty.onePossibleValue(pt) != null) {
+ return;
+ }
+
// We're performing the store at runtime; as such, we need to make sure the pointee type
// is not comptime-only. We can hit this case with a `@ptrFromInt` pointer.
- if (try elem_ty.comptimeOnlySema(pt)) {
+ if (elem_ty.comptimeOnly(zcu)) {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)});
errdefer msg.destroy(sema.gpa);
@@ -30191,12 +29465,6 @@ fn storePtr2(
});
}
- // We do this after the possible comptime store above, for the case of field_ptr stores
- // to unions because we want the comptime tag to be set, even if the field type is void.
- if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
- return;
- }
-
try sema.requireRuntimeBlock(block, src, runtime_src);
const store_inst = if (is_ret)
@@ -30361,10 +29629,10 @@ fn bitCast(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
- try dest_ty.resolveLayout(pt);
-
const old_ty = sema.typeOf(inst);
- try old_ty.resolveLayout(pt);
+
+ old_ty.assertHasLayout(zcu);
+ try sema.ensureLayoutResolved(dest_ty);
const dest_bits = dest_ty.bitSize(zcu);
const old_bits = old_ty.bitSize(zcu);
@@ -30510,9 +29778,7 @@ fn coerceCompatiblePtrs(
}
try sema.requireRuntimeBlock(block, inst_src, null);
const inst_allows_zero = inst_ty.zigTypeTag(zcu) != .pointer or inst_ty.ptrAllowsZero(zcu);
- if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and
- (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn"))
- {
+ if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu)) {
try sema.checkLogicalPtrOperation(block, inst_src, inst_ty);
const actual_ptr = if (inst_ty.isSlice(zcu))
try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
@@ -30532,6 +29798,7 @@ fn coerceCompatiblePtrs(
return new_ptr;
}
+/// Asserts that the layout of `union_ty` is already resolved.
fn coerceEnumToUnion(
sema: *Sema,
block: *Block,
@@ -30545,18 +29812,21 @@ fn coerceEnumToUnion(
const ip = &zcu.intern_pool;
const inst_ty = sema.typeOf(inst);
- const tag_ty = union_ty.unionTagType(zcu) orelse {
- const msg = msg: {
- const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty);
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
- try sema.addDeclaredHereNote(msg, union_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- };
+ union_ty.assertHasLayout(zcu);
- const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
+ const union_obj = zcu.typeToUnion(union_ty).?;
+ const enum_ty: Type = .fromInterned(union_obj.enum_tag_type);
+ const enum_obj = ip.loadEnumType(enum_ty.toIntern());
+
+ if (union_obj.runtime_tag != .tagged) return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty);
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
+ try sema.addDeclaredHereNote(msg, union_ty);
+ break :msg msg;
+ });
+
+ const enum_tag = try sema.coerce(block, enum_ty, inst, inst_src);
if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse {
return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{
@@ -30564,15 +29834,12 @@ fn coerceEnumToUnion(
});
};
- const union_obj = zcu.typeToUnion(union_ty).?;
+ const field_name = enum_obj.field_names.get(ip)[field_index];
const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
- try field_ty.resolveFields(pt);
if (field_ty.zigTypeTag(zcu) == .noreturn) {
const msg = msg: {
const msg = try sema.errMsg(inst_src, "cannot initialize 'noreturn' field of union", .{});
errdefer msg.destroy(sema.gpa);
-
- const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
field_name.fmt(ip),
});
@@ -30581,42 +29848,35 @@ fn coerceEnumToUnion(
};
return sema.failWithOwnedErrorMsg(block, msg);
}
- const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse {
- const msg = msg: {
- const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
- const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{
- inst_ty.fmt(pt), union_ty.fmt(pt),
- field_ty.fmt(pt), field_name.fmt(ip),
- });
- errdefer msg.destroy(sema.gpa);
+ const opv = try field_ty.onePossibleValue(pt) orelse return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{
+ inst_ty.fmt(pt), union_ty.fmt(pt),
+ field_ty.fmt(pt), field_name.fmt(ip),
+ });
+ errdefer msg.destroy(sema.gpa);
- try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
- field_name.fmt(ip),
- });
- try sema.addDeclaredHereNote(msg, union_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- };
+ try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{field_name.fmt(ip)});
+ try sema.addDeclaredHereNote(msg, union_ty);
+ break :msg msg;
+ });
return Air.internedToRef((try pt.unionValue(union_ty, val, opv)).toIntern());
}
try sema.requireRuntimeBlock(block, inst_src, null);
- if (tag_ty.isNonexhaustiveEnum(zcu)) {
+ if (enum_ty.isNonexhaustiveEnum(zcu)) {
const msg = msg: {
const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{
union_ty.fmt(pt),
});
errdefer msg.destroy(sema.gpa);
- try sema.addDeclaredHereNote(msg, tag_ty);
+ try sema.addDeclaredHereNote(msg, enum_ty);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
}
- const union_obj = zcu.typeToUnion(union_ty).?;
{
var msg: ?*Zcu.ErrorMsg = null;
errdefer if (msg) |some| some.destroy(sema.gpa);
@@ -30626,7 +29886,7 @@ fn coerceEnumToUnion(
const err_msg = msg orelse try sema.errMsg(
inst_src,
"runtime coercion from enum '{f}' to union '{f}' which has a 'noreturn' field",
- .{ tag_ty.fmt(pt), union_ty.fmt(pt) },
+ .{ enum_ty.fmt(pt), union_ty.fmt(pt) },
);
msg = err_msg;
@@ -30649,14 +29909,14 @@ fn coerceEnumToUnion(
const msg = try sema.errMsg(
inst_src,
"runtime coercion from enum '{f}' to union '{f}' which has non-void fields",
- .{ tag_ty.fmt(pt), union_ty.fmt(pt) },
+ .{ enum_ty.fmt(pt), union_ty.fmt(pt) },
);
errdefer msg.destroy(sema.gpa);
for (0..union_obj.field_types.len) |field_index| {
- const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
+ const field_name = enum_obj.field_names.get(ip)[field_index];
const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
- if (!(try field_ty.hasRuntimeBitsSema(pt))) continue;
+ if (try field_ty.onePossibleValue(pt) != null) continue;
try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has type '{f}'", .{
field_name.fmt(ip),
field_ty.fmt(pt),
@@ -30904,19 +30164,16 @@ fn coerceTupleToTuple(
const field_i: u32 = @intCast(field_index_usize);
const field_src = inst_src; // TODO better source location
- const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
- .tuple_type => |tuple_type| tuple_type.types.get(ip)[field_index_usize],
- .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize],
- else => unreachable,
- };
- const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
- .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize],
- .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize),
- else => unreachable,
- };
-
const field_index: u32 = @intCast(field_index_usize);
+ const field_ty, const default_val = field: {
+ const tuple_type = ip.indexToKey(tuple_ty.toIntern()).tuple_type;
+ break :field .{
+ tuple_type.types.get(ip)[field_index],
+ tuple_type.values.get(ip)[field_index],
+ };
+ };
+
const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src);
field_refs[field_index] = coerced;
@@ -30946,11 +30203,7 @@ fn coerceTupleToTuple(
const i: u32 = @intCast(i_usize);
if (field_ref.* != .none) continue;
- const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
- .tuple_type => |tuple_type| tuple_type.values.get(ip)[i],
- .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i),
- else => unreachable,
- };
+ const default_val = ip.indexToKey(tuple_ty.toIntern()).tuple_type.values.get(ip)[i];
const field_src = inst_src; // TODO better source location
if (default_val == .none) {
@@ -31019,13 +30272,13 @@ fn addReferenceEntry(
pub fn addTypeReferenceEntry(
sema: *Sema,
src: LazySrcLoc,
- referenced_type: InternPool.Index,
+ referenced_type: Type,
) !void {
const zcu = sema.pt.zcu;
if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return;
- const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
+ const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type.toIntern());
if (gop.found_existing) return;
- try zcu.addTypeReference(sema.owner, referenced_type, src);
+ try zcu.addTypeReference(sema.owner, referenced_type.toIntern(), src);
}
fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void {
@@ -31143,7 +30396,7 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde
.type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
.fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const },
};
- const ptr_ty = try pt.ptrTypeSema(.{
+ const ptr_ty = try pt.ptrType(.{
.child = ty,
.flags = .{
.alignment = alignment,
@@ -31185,7 +30438,7 @@ fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_i
try sema.ensureNavResolved(block, src, nav_index, .type);
const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip));
if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
- if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return;
+ if (!nav_ty.fnHasRuntimeBits(zcu)) return;
try sema.ensureNavResolved(block, src, nav_index, .fully);
const nav_val = zcu.navValue(nav_index);
@@ -31218,14 +30471,14 @@ fn analyzeRef(
// it's just that we can only use the *type* of the result, since the value is runtime-known.
const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local);
- const ptr_type = try pt.ptrTypeSema(.{
+ const ptr_type = try pt.ptrType(.{
.child = operand_ty.toIntern(),
.flags = .{
.is_const = true,
.address_space = address_space,
},
});
- const mut_ptr_type = try pt.ptrTypeSema(.{
+ const mut_ptr_type = try pt.ptrType(.{
.child = operand_ty.toIntern(),
.flags = .{ .address_space = address_space },
});
@@ -31261,9 +30514,8 @@ fn analyzeLoad(
return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)});
}
- if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| {
- return Air.internedToRef(opv.toIntern());
- }
+ try sema.ensureLayoutResolved(elem_ty);
+ if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
@@ -31271,6 +30523,13 @@ fn analyzeLoad(
}
}
+ if (elem_ty.comptimeOnly(zcu)) return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(src, "cannot load comptime-only type '{f}'", .{elem_ty.fmt(pt)});
+ errdefer msg.destroy(zcu.gpa);
+ try sema.errNote(ptr_src, msg, "pointer of type '{f}' is runtime-known", .{ptr_ty.fmt(pt)});
+ break :msg msg;
+ });
+
return block.addTyOp(.load, elem_ty, ptr);
}
@@ -31332,7 +30591,7 @@ fn analyzeSliceLen(
if (slice_val.isUndef(zcu)) {
return .undef_usize;
}
- return pt.intRef(.usize, try slice_val.sliceLen(pt));
+ return pt.intRef(.usize, slice_val.sliceLen(zcu));
}
try sema.requireRuntimeBlock(block, src, null);
return block.addTyOp(.slice_len, .usize, slice_inst);
@@ -31682,6 +30941,8 @@ fn analyzeSlice(
else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}),
}
+ try sema.ensureLayoutResolved(elem_ty);
+
const ptr = if (slice_ty.isSlice(zcu))
try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
else if (array_ty.zigTypeTag(zcu) == .array) ptr: {
@@ -31690,7 +30951,7 @@ fn analyzeSlice(
assert(manyptr_ty_key.flags.size == .one);
manyptr_ty_key.child = elem_ty.toIntern();
manyptr_ty_key.flags.size = .many;
- break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(manyptr_ty_key), ptr_or_slice, ptr_src);
+ break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src);
} else ptr_or_slice;
const start = try sema.coerce(block, .usize, uncasted_start, start_src);
@@ -31759,7 +31020,7 @@ fn analyzeSlice(
return sema.fail(block, src, "slice of undefined", .{});
}
const has_sentinel = slice_ty.sentinel(zcu) != null;
- const slice_len = try slice_val.sliceLen(pt);
+ const slice_len = slice_val.sliceLen(zcu);
const len_plus_sent = slice_len + @intFromBool(has_sentinel);
const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent);
if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) {
@@ -31774,7 +31035,7 @@ fn analyzeSlice(
"end index {f} out of bounds for slice of length {d}{s}",
.{
end_val.fmtValueSema(pt, sema),
- try slice_val.sliceLen(pt),
+ slice_val.sliceLen(zcu),
sentinel_label,
},
);
@@ -31943,9 +31204,9 @@ fn analyzeSlice(
const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c;
if (opt_new_len_val) |new_len_val| {
- const new_len_int = try new_len_val.toUnsignedIntSema(pt);
+ const new_len_int = new_len_val.toUnsignedInt(zcu);
- const return_ty = try pt.ptrTypeSema(.{
+ const return_ty = try pt.ptrType(.{
.child = (try pt.arrayType(.{
.len = new_len_int,
.sentinel = if (sentinel) |s| s.toIntern() else .none,
@@ -32009,7 +31270,7 @@ fn analyzeSlice(
return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
}
- const return_ty = try pt.ptrTypeSema(.{
+ const return_ty = try pt.ptrType(.{
.child = elem_ty.toIntern(),
.sentinel = if (sentinel) |s| s.toIntern() else .none,
.flags = .{
@@ -32037,7 +31298,7 @@ fn analyzeSlice(
if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
// we don't need to add one for sentinels because the
// underlying value data includes the sentinel
- break :blk try pt.intRef(.usize, try slice_val.sliceLen(pt));
+ break :blk try pt.intRef(.usize, slice_val.sliceLen(zcu));
}
const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice);
@@ -32158,16 +31419,10 @@ fn cmpNumeric(
const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: {
if (maybe_rhs_val) |rhs_val| {
- const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt);
- return if (res) .bool_true else .bool_false;
+ return .fromValue(.makeBool(Value.compareHetero(lhs_val, op, rhs_val, zcu)));
} else break :rs rhs_src;
} else lhs_src;
- // TODO handle comparisons against lazy zero values
- // Some values can be compared against zero without being runtime-known or without forcing
- // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
- // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
- // of this function if we don't need to.
try sema.requireRuntimeBlock(block, src, runtime_src);
// For floats, emit a float comparison instruction.
@@ -32207,11 +31462,11 @@ fn cmpNumeric(
// a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
// add/subtract 1.
const lhs_is_signed = if (maybe_lhs_val) |lhs_val|
- !(try lhs_val.compareAllWithZeroSema(.gte, pt))
+ !lhs_val.compareAllWithZero(.gte, zcu)
else
(lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu));
const rhs_is_signed = if (maybe_rhs_val) |rhs_val|
- !(try rhs_val.compareAllWithZeroSema(.gte, pt))
+ !rhs_val.compareAllWithZero(.gte, zcu)
else
(rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));
const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
@@ -32219,10 +31474,9 @@ fn cmpNumeric(
var dest_float_type: ?Type = null;
var lhs_bits: usize = undefined;
- if (maybe_lhs_val) |unresolved_lhs_val| {
- const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val);
+ if (maybe_lhs_val) |lhs_val| {
if (!rhs_is_signed) {
- switch (lhs_val.orderAgainstZero(zcu)) {
+ switch (Value.order(lhs_val, .zero_comptime_int, zcu)) {
.gt => {},
.eq => switch (op) { // LHS = 0, RHS is unsigned
.lte => return .bool_true,
@@ -32263,10 +31517,9 @@ fn cmpNumeric(
}
var rhs_bits: usize = undefined;
- if (maybe_rhs_val) |unresolved_rhs_val| {
- const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val);
+ if (maybe_rhs_val) |rhs_val| {
if (!lhs_is_signed) {
- switch (rhs_val.orderAgainstZero(zcu)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {},
.eq => switch (op) { // RHS = 0, LHS is unsigned
.gte => return .bool_true,
@@ -32328,7 +31581,7 @@ fn compareIntsOnlyPossibleResult(
lhs_val: Value,
op: std.math.CompareOperator,
rhs_ty: Type,
-) SemaError!?bool {
+) Allocator.Error!?bool {
const pt = sema.pt;
const zcu = pt.zcu;
@@ -32337,11 +31590,11 @@ fn compareIntsOnlyPossibleResult(
if (min_rhs.toIntern() == max_rhs.toIntern()) {
// RHS is effectively comptime-known.
- return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt);
+ return Value.compareHetero(lhs_val, op, min_rhs, zcu);
}
- const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid);
- const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid);
+ const against_min = lhs_val.order(min_rhs, zcu);
+ const against_max = lhs_val.order(max_rhs, zcu);
switch (op) {
.eq => {
@@ -32529,9 +31782,7 @@ fn unionToTag(
) !Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
- if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| {
- return Air.internedToRef(opv.toIntern());
- }
+ if (try enum_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
if (try sema.resolveValue(un)) |un_val| {
const tag_val = un_val.unionTag(zcu).?;
if (tag_val.isUndef(zcu))
@@ -33240,18 +32491,24 @@ fn resolvePeerTypesInner(
ptr_info.sentinel = .none;
}
- // Note that the align can be always non-zero; Zcu.ptrType will canonicalize it
- ptr_info.flags.alignment = InternPool.Alignment.min(
- if (ptr_info.flags.alignment != .none)
- ptr_info.flags.alignment
- else
- Type.fromInterned(ptr_info.child).abiAlignment(zcu),
-
- if (peer_info.flags.alignment != .none)
- peer_info.flags.alignment
- else
- Type.fromInterned(peer_info.child).abiAlignment(zcu),
- );
+ ptr_info.flags.alignment = a: {
+ // If both alignments are implicit, the result alignment is implicit.
+ // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]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'
+ const cur_align = switch (ptr_info.flags.alignment) {
+ .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu),
+ else => ptr_info.flags.alignment,
+ };
+ const new_align = switch (peer_info.flags.alignment) {
+ .none => Type.fromInterned(peer_info.child).abiAlignment(zcu),
+ else => peer_info.flags.alignment,
+ };
+ break :a .minStrict(cur_align, new_align);
+ };
if (ptr_info.flags.address_space != peer_info.flags.address_space) {
return .{ .conflict = .{
.peer_idx_a = first_idx,
@@ -33273,7 +32530,7 @@ fn resolvePeerTypesInner(
opt_ptr_info = ptr_info;
}
- return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) };
+ return .{ .success = try pt.ptrType(opt_ptr_info.?) };
},
.ptr => {
@@ -33281,7 +32538,6 @@ fn resolvePeerTypesInner(
// if there were no actual slices. Else, we want the slice index to report a conflict.
var opt_slice_idx: ?usize = null;
- var any_abi_aligned = false;
var opt_ptr_info: ?InternPool.Key.PtrType = null;
var first_idx: usize = undefined;
var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
@@ -33325,15 +32581,24 @@ fn resolvePeerTypesInner(
.peer_idx_b = i,
} };
- // Note that the align can be always non-zero; Type.ptr will canonicalize it
- if (peer_info.flags.alignment == .none) {
- any_abi_aligned = true;
- } else if (ptr_info.flags.alignment == .none) {
- any_abi_aligned = true;
- ptr_info.flags.alignment = peer_info.flags.alignment;
- } else {
- ptr_info.flags.alignment = ptr_info.flags.alignment.minStrict(peer_info.flags.alignment);
- }
+ ptr_info.flags.alignment = a: {
+ // If both alignments are implicit, the result alignment is implicit.
+ // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]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'
+ const cur_align = switch (ptr_info.flags.alignment) {
+ .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu),
+ else => ptr_info.flags.alignment,
+ };
+ const new_align = switch (peer_info.flags.alignment) {
+ .none => Type.fromInterned(peer_info.child).abiAlignment(zcu),
+ else => peer_info.flags.alignment,
+ };
+ break :a .minStrict(cur_align, new_align);
+ };
if (ptr_info.flags.address_space != peer_info.flags.address_space) {
return generic_err;
@@ -33582,13 +32847,7 @@ fn resolvePeerTypesInner(
},
}
- if (any_abi_aligned and opt_ptr_info.?.flags.alignment != .none) {
- opt_ptr_info.?.flags.alignment = opt_ptr_info.?.flags.alignment.minStrict(
- try Type.fromInterned(pointee).abiAlignmentSema(pt),
- );
- }
-
- return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) };
+ return .{ .success = try pt.ptrType(opt_ptr_info.?) };
},
.func => {
@@ -33731,7 +32990,7 @@ fn resolvePeerTypesInner(
.peer_idx_b = i,
} };
any_comptime_known = true;
- ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
+ ptr_opt_val.* = opt_val.?;
continue;
},
.int => {},
@@ -33924,7 +33183,6 @@ fn resolvePeerTypesInner(
var comptime_val: ?Value = null;
for (peer_tys) |opt_ty| {
const struct_ty = opt_ty orelse continue;
- try struct_ty.resolveStructFieldInits(pt);
const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse {
comptime_val = null;
@@ -34058,344 +33316,6 @@ pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void
}
}
-pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
- const fn_ty_info = zcu.typeToFunc(fn_ty).?;
-
- try Type.fromInterned(fn_ty_info.return_type).resolveFully(pt);
-
- if (zcu.comp.config.any_error_tracing and
- Type.fromInterned(fn_ty_info.return_type).isError(zcu))
- {
- // Ensure the type exists so that backends can assume that.
- _ = try sema.getBuiltinType(src, .StackTrace);
- }
-
- for (0..fn_ty_info.param_types.len) |i| {
- try Type.fromInterned(fn_ty_info.param_types.get(ip)[i]).resolveFully(pt);
- }
-}
-
-fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
- return val.resolveLazy(sema.arena, sema.pt);
-}
-
-/// Resolve a struct's alignment only without triggering resolution of its layout.
-/// Asserts that the alignment is not yet resolved and the layout is non-packed.
-pub fn resolveStructAlignment(
- sema: *Sema,
- ty: InternPool.Index,
- struct_type: InternPool.LoadedStructType,
-) SemaError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
- const target = zcu.getTarget();
-
- assert(sema.owner.unwrap().type == ty);
-
- assert(struct_type.layout != .@"packed");
- assert(struct_type.flagsUnordered(ip).alignment == .none);
-
- const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
-
- // We'll guess "pointer-aligned", if the struct has an
- // underaligned pointer field then some allocations
- // might require explicit alignment.
- if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, io, ptr_align)) return;
-
- try sema.resolveStructFieldTypes(ty, struct_type);
-
- // We'll guess "pointer-aligned", if the struct has an
- // underaligned pointer field then some allocations
- // might require explicit alignment.
- if (struct_type.assumePointerAlignedIfWip(ip, io, ptr_align)) return;
- defer struct_type.clearAlignmentWip(ip, io);
-
- // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
- // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
-
- var alignment: Alignment = .@"1";
-
- for (0..struct_type.field_types.len) |i| {
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt))
- continue;
- const field_align = try field_ty.structFieldAlignmentSema(
- struct_type.fieldAlign(ip, i),
- struct_type.layout,
- pt,
- );
- alignment = alignment.maxStrict(field_align);
- }
-
- struct_type.setAlignment(ip, io, alignment);
-}
-
-pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
- const io = zcu.comp.io;
- const struct_type = zcu.typeToStruct(ty) orelse return;
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- if (struct_type.haveLayout(ip))
- return;
-
- try sema.resolveStructFieldTypes(ty.toIntern(), struct_type);
-
- // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
- // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
-
- if (struct_type.layout == .@"packed") {
- sema.backingIntType(struct_type) catch |err| switch (err) {
- error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
- error.ComptimeBreak, error.ComptimeReturn => unreachable,
- };
- return;
- }
-
- if (struct_type.setLayoutWip(ip, io)) {
- const msg = try sema.errMsg(
- ty.srcLoc(zcu),
- "struct '{f}' depends on itself",
- .{ty.fmt(pt)},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
- defer struct_type.clearLayoutWip(ip, io);
-
- const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);
- const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);
-
- var big_align: Alignment = .@"1";
-
- for (aligns, sizes, 0..) |*field_align, *field_size, i| {
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) {
- struct_type.offsets.get(ip)[i] = 0;
- field_size.* = 0;
- field_align.* = .none;
- continue;
- }
-
- field_size.* = field_ty.abiSizeSema(pt) catch |err| switch (err) {
- error.AnalysisFail => {
- const msg = sema.err orelse return err;
- try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
- return err;
- },
- else => return err,
- };
- field_align.* = try field_ty.structFieldAlignmentSema(
- struct_type.fieldAlign(ip, i),
- struct_type.layout,
- pt,
- );
- big_align = big_align.maxStrict(field_align.*);
- }
-
- if (struct_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
- const msg = try sema.errMsg(
- ty.srcLoc(zcu),
- "struct layout depends on it having runtime bits",
- .{},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
-
- if (struct_type.flagsUnordered(ip).assumed_pointer_aligned and
- big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(zcu.getTarget().ptrBitWidth(), 8))))
- {
- const msg = try sema.errMsg(
- ty.srcLoc(zcu),
- "struct layout depends on being pointer aligned",
- .{},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
-
- if (struct_type.hasReorderedFields()) {
- const runtime_order = struct_type.runtime_order.get(ip);
-
- for (runtime_order, 0..) |*ro, i| {
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) {
- ro.* = .omitted;
- } else {
- ro.* = @enumFromInt(i);
- }
- }
-
- const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder;
-
- const AlignSortContext = struct {
- aligns: []const Alignment,
-
- fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool {
- if (a == .omitted) return false;
- if (b == .omitted) return true;
- const a_align = ctx.aligns[@intFromEnum(a)];
- const b_align = ctx.aligns[@intFromEnum(b)];
- return a_align.compare(.gt, b_align);
- }
- };
- if (!zcu.backendSupportsFeature(.field_reordering)) {
- // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve
- // mutating the `InternPool` for a non-container type.
- //
- // TODO: implement field reordering support in all the backends!
- //
- // This logic does not reorder fields; it only moves the omitted ones to the end
- // so that logic elsewhere does not need to special-case here.
- var i: usize = 0;
- var off: usize = 0;
- while (i + off < runtime_order.len) {
- if (runtime_order[i + off] == .omitted) {
- off += 1;
- continue;
- }
- runtime_order[i] = runtime_order[i + off];
- i += 1;
- }
- @memset(runtime_order[i..], .omitted);
- } else {
- mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{
- .aligns = aligns,
- }, AlignSortContext.lessThan);
- }
- }
-
- // Calculate size, alignment, and field offsets.
- const offsets = struct_type.offsets.get(ip);
- var it = struct_type.iterateRuntimeOrder(ip);
- var offset: u64 = 0;
- while (it.next()) |i| {
- offsets[i] = @intCast(aligns[i].forward(offset));
- offset = offsets[i] + sizes[i];
- }
- const size = std.math.cast(u32, big_align.forward(offset)) orelse {
- const msg = try sema.errMsg(
- ty.srcLoc(zcu),
- "struct layout requires size {d}, this compiler implementation supports up to {d}",
- .{ big_align.forward(offset), std.math.maxInt(u32) },
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- };
- struct_type.setLayoutResolved(ip, io, size, big_align);
- _ = try ty.comptimeOnlySema(pt);
-}
-
-fn backingIntType(
- sema: *Sema,
- struct_type: InternPool.LoadedStructType,
-) CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- var analysis_arena = std.heap.ArenaAllocator.init(gpa);
- defer analysis_arena.deinit();
-
- var block: Block = .{
- .parent = null,
- .sema = sema,
- .namespace = struct_type.namespace,
- .instructions = .{},
- .inlining = null,
- .comptime_reason = null, // set below if needed
- .src_base_inst = struct_type.zir_index,
- .type_name_ctx = struct_type.name,
- };
- defer assert(block.instructions.items.len == 0);
-
- const fields_bit_sum = blk: {
- var accumulator: u64 = 0;
- for (0..struct_type.field_types.len) |i| {
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- accumulator += try field_ty.bitSizeSema(pt);
- }
- break :blk accumulator;
- };
-
- const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?;
- const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
- const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
-
- if (small.has_backing_int) {
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- extra_index += @intFromBool(small.has_fields_len);
- extra_index += @intFromBool(small.has_decls_len);
-
- extra_index += captures_len * 2;
-
- const backing_int_body_len = zir.extra[extra_index];
- extra_index += 1;
-
- const backing_int_src: LazySrcLoc = .{
- .base_node_inst = struct_type.zir_index,
- .offset = .{ .node_offset_container_tag = .zero },
- };
- block.comptime_reason = .{ .reason = .{
- .src = backing_int_src,
- .r = .{ .simple = .type },
- } };
- const backing_int_ty = blk: {
- if (backing_int_body_len == 0) {
- const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
- } else {
- const body = zir.bodySlice(extra_index, backing_int_body_len);
- const ty_ref = try sema.resolveInlineBody(&block, body, zir_index);
- break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
- }
- };
-
- try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
- struct_type.setBackingIntType(ip, io, backing_int_ty.toIntern());
- } else {
- if (fields_bit_sum > std.math.maxInt(u16)) {
- return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
- }
- const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
- struct_type.setBackingIntType(ip, io, backing_int_ty.toIntern());
- }
-
- try sema.flushExports();
-}
-
-fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
-
- if (!backing_int_ty.isInt(zcu)) {
- return sema.fail(block, src, "expected backing integer type, found '{f}'", .{backing_int_ty.fmt(pt)});
- }
- if (backing_int_ty.bitSize(zcu) != fields_bit_sum) {
- return sema.fail(
- block,
- src,
- "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}",
- .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum },
- );
- }
-}
-
fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
const pt = sema.pt;
if (!ty.isIndexable(pt.zcu)) {
@@ -34432,358 +33352,6 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void
return sema.failWithOwnedErrorMsg(block, msg);
}
-/// Resolve a unions's alignment only without triggering resolution of its layout.
-/// Asserts that the alignment is not yet resolved.
-pub fn resolveUnionAlignment(
- sema: *Sema,
- ty: Type,
- union_type: InternPool.LoadedUnionType,
-) SemaError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
- const target = zcu.getTarget();
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- assert(!union_type.haveLayout(ip));
-
- const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
-
- // We'll guess "pointer-aligned", if the union has an
- // underaligned pointer field then some allocations
- // might require explicit alignment.
- if (union_type.assumePointerAlignedIfFieldTypesWip(ip, io, ptr_align)) return;
-
- try sema.resolveUnionFieldTypes(ty, union_type);
-
- // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
- // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
-
- var max_align: Alignment = .@"1";
- for (0..union_type.field_types.len) |field_index| {
- const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
- if (!(try field_ty.hasRuntimeBitsSema(pt))) continue;
-
- const explicit_align = union_type.fieldAlign(ip, field_index);
- const field_align = if (explicit_align != .none)
- explicit_align
- else
- try field_ty.abiAlignmentSema(sema.pt);
-
- max_align = max_align.max(field_align);
- }
-
- union_type.setAlignment(ip, io, max_align);
-}
-
-/// This logic must be kept in sync with `Type.getUnionLayout`.
-pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
- const pt = sema.pt;
- const io = pt.zcu.comp.io;
- const ip = &pt.zcu.intern_pool;
-
- try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index));
-
- // Load again, since the tag type might have changed due to resolution.
- const union_type = ip.loadUnionType(ty.ip_index);
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- const old_flags = union_type.flagsUnordered(ip);
- switch (old_flags.status) {
- .none, .have_field_types => {},
- .field_types_wip, .layout_wip => {
- const msg = try sema.errMsg(
- ty.srcLoc(pt.zcu),
- "union '{f}' depends on itself",
- .{ty.fmt(pt)},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- },
- .have_layout, .fully_resolved_wip, .fully_resolved => return,
- }
-
- errdefer union_type.setStatusIfLayoutWip(ip, io, old_flags.status);
-
- union_type.setStatus(ip, io, .layout_wip);
-
- // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
- // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
-
- var max_size: u64 = 0;
- var max_align: Alignment = .@"1";
- for (0..union_type.field_types.len) |field_index| {
- const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
- if (field_ty.isNoReturn(pt.zcu)) continue;
-
- // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s,
- // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type,
- // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us.
- // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to
- // be queried, enabling failures to be handled with the emission of a compile error, and also in
- // `hasRuntimeBits` for ever being able to infinite recurse in the first place.
- try field_ty.resolveLayout(pt);
-
- if (try field_ty.hasRuntimeBitsSema(pt)) {
- max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
- error.AnalysisFail => {
- const msg = sema.err orelse return err;
- try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
- return err;
- },
- else => return err,
- });
- }
-
- const explicit_align = union_type.fieldAlign(ip, field_index);
- const field_align = if (explicit_align != .none)
- explicit_align
- else
- try field_ty.abiAlignmentSema(pt);
- max_align = max_align.max(field_align);
- }
-
- const has_runtime_tag = union_type.flagsUnordered(ip).runtime_tag.hasTag() and
- try Type.fromInterned(union_type.enum_tag_ty).hasRuntimeBitsSema(pt);
- const size, const alignment, const padding = if (has_runtime_tag) layout: {
- const enum_tag_type: Type = .fromInterned(union_type.enum_tag_ty);
- const tag_align = try enum_tag_type.abiAlignmentSema(pt);
- const tag_size = try enum_tag_type.abiSizeSema(pt);
-
- // Put the tag before or after the payload depending on which one's
- // alignment is greater.
- var size: u64 = 0;
- var padding: u32 = 0;
- if (tag_align.order(max_align).compare(.gte)) {
- // {Tag, Payload}
- size += tag_size;
- size = max_align.forward(size);
- size += max_size;
- const prev_size = size;
- size = tag_align.forward(size);
- padding = @intCast(size - prev_size);
- } else {
- // {Payload, Tag}
- size += max_size;
- size = switch (pt.zcu.getTarget().ofmt) {
- .c => max_align,
- else => tag_align,
- }.forward(size);
- size += tag_size;
- const prev_size = size;
- size = max_align.forward(size);
- padding = @intCast(size - prev_size);
- }
-
- break :layout .{ size, max_align.max(tag_align), padding };
- } else .{ max_align.forward(max_size), max_align, 0 };
-
- const casted_size = std.math.cast(u32, size) orelse {
- const msg = try sema.errMsg(
- ty.srcLoc(pt.zcu),
- "union layout requires size {d}, this compiler implementation supports up to {d}",
- .{ size, std.math.maxInt(u32) },
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- };
- union_type.setHaveLayout(ip, io, casted_size, padding, alignment);
-
- if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
- const msg = try sema.errMsg(
- ty.srcLoc(pt.zcu),
- "union layout depends on it having runtime bits",
- .{},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
-
- if (union_type.flagsUnordered(ip).assumed_pointer_aligned and
- alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(pt.zcu.getTarget().ptrBitWidth(), 8))))
- {
- const msg = try sema.errMsg(
- ty.srcLoc(pt.zcu),
- "union layout depends on being pointer aligned",
- .{},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
- _ = try ty.comptimeOnlySema(pt);
-}
-
-/// Returns `error.AnalysisFail` if any of the types (recursively) failed to
-/// be resolved.
-pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void {
- try sema.resolveStructLayout(ty);
- try sema.resolveStructFieldInits(ty);
-
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
- const struct_type = zcu.typeToStruct(ty).?;
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- if (struct_type.setFullyResolved(ip, io)) return;
- errdefer struct_type.clearFullyResolved(ip, io);
-
- // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
- // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
-
- // After we have resolve struct layout we have to go over the fields again to
- // make sure pointer fields get their child types resolved as well.
- // See also similar code for unions.
-
- for (0..struct_type.field_types.len) |i| {
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- try field_ty.resolveFully(pt);
- }
-}
-
-pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
- try sema.resolveUnionLayout(ty);
-
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
- const union_obj = zcu.typeToUnion(ty).?;
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- switch (union_obj.flagsUnordered(ip).status) {
- .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
- .fully_resolved_wip, .fully_resolved => return,
- }
-
- // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
- // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
-
- {
- // After we have resolve union layout we have to go over the fields again to
- // make sure pointer fields get their child types resolved as well.
- // See also similar code for structs.
- const prev_status = union_obj.flagsUnordered(ip).status;
- errdefer union_obj.setStatus(ip, io, prev_status);
-
- union_obj.setStatus(ip, io, .fully_resolved_wip);
- for (0..union_obj.field_types.len) |field_index| {
- const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
- try field_ty.resolveFully(pt);
- }
- union_obj.setStatus(ip, io, .fully_resolved);
- }
-
- // And let's not forget comptime-only status.
- _ = try ty.comptimeOnlySema(pt);
-}
-
-pub fn resolveStructFieldTypes(
- sema: *Sema,
- ty: InternPool.Index,
- struct_type: InternPool.LoadedStructType,
-) SemaError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
-
- assert(sema.owner.unwrap().type == ty);
-
- if (struct_type.haveFieldTypes(ip)) return;
-
- if (struct_type.setFieldTypesWip(ip, io)) {
- const msg = try sema.errMsg(
- Type.fromInterned(ty).srcLoc(zcu),
- "struct '{f}' depends on itself",
- .{Type.fromInterned(ty).fmt(pt)},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
- defer struct_type.clearFieldTypesWip(ip, io);
-
- // can't happen earlier than this because we only want the progress node if not already resolved
- const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null);
- defer tracked_unit.end(zcu);
-
- sema.structFields(struct_type) catch |err| switch (err) {
- error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
- error.ComptimeBreak, error.ComptimeReturn => unreachable,
- };
-}
-
-pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
- const struct_type = zcu.typeToStruct(ty) orelse return;
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- // Inits can start as resolved
- if (struct_type.haveFieldInits(ip)) return;
-
- try sema.resolveStructLayout(ty);
-
- if (struct_type.setInitsWip(ip, io)) {
- const msg = try sema.errMsg(
- ty.srcLoc(zcu),
- "struct '{f}' depends on itself",
- .{ty.fmt(pt)},
- );
- return sema.failWithOwnedErrorMsg(null, msg);
- }
- defer struct_type.clearInitsWip(ip, io);
-
- // can't happen earlier than this because we only want the progress node if not already resolved
- const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null);
- defer tracked_unit.end(zcu);
-
- sema.structFieldInits(struct_type) catch |err| switch (err) {
- error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
- error.ComptimeBreak, error.ComptimeReturn => unreachable,
- };
- struct_type.setHaveFieldInits(ip, io);
-}
-
-pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
-
- assert(sema.owner.unwrap().type == ty.toIntern());
-
- switch (union_type.flagsUnordered(ip).status) {
- .none => {},
- .field_types_wip => {
- const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)});
- return sema.failWithOwnedErrorMsg(null, msg);
- },
- .have_field_types,
- .have_layout,
- .layout_wip,
- .fully_resolved_wip,
- .fully_resolved,
- => return,
- }
-
- // can't happen earlier than this because we only want the progress node if not already resolved
- const tracked_unit = zcu.trackUnitSema(union_type.name.toSlice(ip), null);
- defer tracked_unit.end(zcu);
-
- union_type.setStatus(ip, io, .field_types_wip);
- errdefer union_type.setStatus(ip, io, .none);
- sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) {
- error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
- error.ComptimeBreak, error.ComptimeReturn => unreachable,
- };
- union_type.setStatus(ip, io, .have_field_types);
-}
-
/// Returns a normal error set corresponding to the fully populated inferred
/// error set.
fn resolveInferredErrorSet(
@@ -34798,8 +33366,9 @@ fn resolveInferredErrorSet(
const func_index = ip.iesFuncIndex(ies_index);
const func = zcu.funcInfo(func_index);
- try sema.declareDependency(.{ .interned = func_index }); // resolved IES
+ try sema.declareDependency(.{ .func_ies = func_index });
+ // MLUGG TODO: this feels kinda bad now... instead check for outdated whenver we grab this?
try zcu.maybeUnresolveIes(func_index);
const resolved_ty = func.resolvedErrorSetUnordered(ip);
if (resolved_ty != .none) return resolved_ty;
@@ -34820,7 +33389,7 @@ fn resolveInferredErrorSet(
if (ies_func_info.return_type == .generic_poison_type) {
assert(ies_func_info.cc == .@"inline");
} else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) {
- if (ies_func_info.is_generic) {
+ if (!Type.fromInterned(func.ty).fnHasRuntimeBits(zcu)) {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{});
errdefer msg.destroy(sema.gpa);
@@ -34935,1248 +33504,6 @@ fn resolveInferredErrorSetTy(
}
}
-fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
- /// fields_len
- usize,
- Zir.Inst.StructDecl.Small,
- /// extra_index
- usize,
-} {
- const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
-
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) decls_len: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :decls_len decls_len;
- } else 0;
-
- extra_index += captures_len * 2;
-
- // The backing integer cannot be handled until `resolveStructLayout()`.
- if (small.has_backing_int) {
- const backing_int_body_len = zir.extra[extra_index];
- extra_index += 1; // backing_int_body_len
- if (backing_int_body_len == 0) {
- extra_index += 1; // backing_int_ref
- } else {
- extra_index += backing_int_body_len; // backing_int_body_inst
- }
- }
-
- // Skip over decls.
- extra_index += decls_len;
-
- return .{ fields_len, small, extra_index };
-}
-
-fn structFields(
- sema: *Sema,
- struct_type: InternPool.LoadedStructType,
-) CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const namespace_index = struct_type.namespace;
- const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
- const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
-
- const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
-
- if (fields_len == 0) switch (struct_type.layout) {
- .@"packed" => {
- try sema.backingIntType(struct_type);
- return;
- },
- .auto, .@"extern" => {
- struct_type.setLayoutResolved(ip, io, 0, .none);
- return;
- },
- };
-
- var block_scope: Block = .{
- .parent = null,
- .sema = sema,
- .namespace = namespace_index,
- .instructions = .{},
- .inlining = null,
- .comptime_reason = .{ .reason = .{
- .src = .{
- .base_node_inst = struct_type.zir_index,
- .offset = .nodeOffset(.zero),
- },
- .r = .{ .simple = .type },
- } },
- .src_base_inst = struct_type.zir_index,
- .type_name_ctx = struct_type.name,
- };
- defer assert(block_scope.instructions.items.len == 0);
-
- const Field = struct {
- type_body_len: u32 = 0,
- align_body_len: u32 = 0,
- init_body_len: u32 = 0,
- type_ref: Zir.Inst.Ref = .none,
- };
- const fields = try sema.arena.alloc(Field, fields_len);
-
- var any_inits = false;
- var any_aligned = false;
-
- {
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- const flags_index = extra_index;
- var bit_bag_index: usize = flags_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
-
- if (is_comptime) struct_type.setFieldComptime(ip, field_i);
-
- const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index]));
- extra_index += 1; // field_name
-
- fields[field_i] = .{};
-
- if (has_type_body) {
- fields[field_i].type_body_len = zir.extra[extra_index];
- } else {
- fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]);
- }
- extra_index += 1;
-
- // This string needs to outlive the ZIR code.
- const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls);
- assert(struct_type.addFieldName(ip, field_name) == null);
-
- if (has_align) {
- fields[field_i].align_body_len = zir.extra[extra_index];
- extra_index += 1;
- any_aligned = true;
- }
- if (has_init) {
- fields[field_i].init_body_len = zir.extra[extra_index];
- extra_index += 1;
- any_inits = true;
- }
- }
- }
-
- // Next we do only types and alignments, saving the inits for a second pass,
- // so that init values may depend on type layout.
-
- for (fields, 0..) |zir_field, field_i| {
- const ty_src: LazySrcLoc = .{
- .base_node_inst = struct_type.zir_index,
- .offset = .{ .container_field_type = @intCast(field_i) },
- };
- const field_ty: Type = ty: {
- if (zir_field.type_ref != .none) {
- break :ty try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
- }
- assert(zir_field.type_body_len != 0);
- const body = zir.bodySlice(extra_index, zir_field.type_body_len);
- extra_index += body.len;
- const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
- break :ty try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
- };
-
- struct_type.field_types.get(ip)[field_i] = field_ty.toIntern();
-
- if (field_ty.zigTypeTag(zcu) == .@"opaque") {
- const msg = msg: {
- const msg = try sema.errMsg(ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
- errdefer msg.destroy(sema.gpa);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- if (field_ty.zigTypeTag(zcu) == .noreturn) {
- const msg = msg: {
- const msg = try sema.errMsg(ty_src, "struct fields cannot be 'noreturn'", .{});
- errdefer msg.destroy(sema.gpa);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- switch (struct_type.layout) {
- .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) {
- const msg = msg: {
- const msg = try sema.errMsg(ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- },
- .@"packed" => if (!try sema.validatePackedType(field_ty)) {
- const msg = msg: {
- const msg = try sema.errMsg(ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- },
- else => {},
- }
-
- if (zir_field.align_body_len > 0) {
- const body = zir.bodySlice(extra_index, zir_field.align_body_len);
- extra_index += body.len;
- const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
- const align_src: LazySrcLoc = .{
- .base_node_inst = struct_type.zir_index,
- .offset = .{ .container_field_align = @intCast(field_i) },
- };
- const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
- struct_type.field_aligns.get(ip)[field_i] = field_align;
- }
-
- extra_index += zir_field.init_body_len;
- }
-
- struct_type.clearFieldTypesWip(ip, io);
- if (!any_inits) struct_type.setHaveFieldInits(ip, io);
-
- try sema.flushExports();
-}
-
-// This logic must be kept in sync with `structFields`
-fn structFieldInits(
- sema: *Sema,
- struct_type: InternPool.LoadedStructType,
-) CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
-
- assert(!struct_type.haveFieldInits(ip));
-
- const namespace_index = struct_type.namespace;
- const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
- const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
- const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
-
- var block_scope: Block = .{
- .parent = null,
- .sema = sema,
- .namespace = namespace_index,
- .instructions = .{},
- .inlining = null,
- .comptime_reason = undefined, // set when `block_scope` is used
- .src_base_inst = struct_type.zir_index,
- .type_name_ctx = struct_type.name,
- };
- defer assert(block_scope.instructions.items.len == 0);
-
- const Field = struct {
- type_body_len: u32 = 0,
- align_body_len: u32 = 0,
- init_body_len: u32 = 0,
- };
- const fields = try sema.arena.alloc(Field, fields_len);
-
- var any_inits = false;
-
- {
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- const flags_index = extra_index;
- var bit_bag_index: usize = flags_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 2;
- const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
-
- extra_index += 1; // field_name
-
- fields[field_i] = .{};
-
- if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index];
- extra_index += 1;
-
- if (has_align) {
- fields[field_i].align_body_len = zir.extra[extra_index];
- extra_index += 1;
- }
- if (has_init) {
- fields[field_i].init_body_len = zir.extra[extra_index];
- extra_index += 1;
- any_inits = true;
- }
- }
- }
-
- if (any_inits) {
- for (fields, 0..) |zir_field, field_i| {
- extra_index += zir_field.type_body_len;
- extra_index += zir_field.align_body_len;
- const body = zir.bodySlice(extra_index, zir_field.init_body_len);
- extra_index += zir_field.init_body_len;
-
- if (body.len == 0) continue;
-
- // Pre-populate the type mapping the body expects to be there.
- // In init bodies, the zir index of the struct itself is used
- // to refer to the current field type.
-
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_i]);
- const type_ref = Air.internedToRef(field_ty.toIntern());
- try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
- sema.inst_map.putAssumeCapacity(zir_index, type_ref);
-
- const init_src: LazySrcLoc = .{
- .base_node_inst = struct_type.zir_index,
- .offset = .{ .container_field_value = @intCast(field_i) },
- };
-
- block_scope.comptime_reason = .{ .reason = .{
- .src = init_src,
- .r = .{ .simple = .struct_field_default_value },
- } };
- const init = try sema.resolveInlineBody(&block_scope, body, zir_index);
- const coerced = try sema.coerce(&block_scope, field_ty, init, init_src);
- const default_val = try sema.resolveConstValue(&block_scope, init_src, coerced, null);
-
- if (default_val.canMutateComptimeVarState(zcu)) {
- return sema.failWithContainsReferenceToComptimeVar(
- &block_scope,
- init_src,
- struct_type.fieldName(ip, field_i),
- "field default value",
- default_val,
- );
- }
- struct_type.field_inits.get(ip)[field_i] = default_val.toIntern();
- }
- }
-
- try sema.flushExports();
-}
-
-fn unionFields(
- sema: *Sema,
- union_ty: InternPool.Index,
- union_type: InternPool.LoadedUnionType,
-) CompileError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?;
- const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
- const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
- assert(extended.opcode == .union_decl);
- const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
- var extra_index: usize = extra.end;
-
- const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
- const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- extra_index += 1;
- break :blk ty_ref;
- } else .none;
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
-
- const body_len = if (small.has_body_len) blk: {
- const body_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) decls_len: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :decls_len decls_len;
- } else 0;
-
- // Skip over captures and decls.
- extra_index += captures_len * 2 + decls_len;
-
- const body = zir.bodySlice(extra_index, body_len);
- extra_index += body.len;
-
- const src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .nodeOffset(.zero),
- };
-
- var block_scope: Block = .{
- .parent = null,
- .sema = sema,
- .namespace = union_type.namespace,
- .instructions = .{},
- .inlining = null,
- .comptime_reason = .{ .reason = .{
- .src = src,
- .r = .{ .simple = .type },
- } },
- .src_base_inst = union_type.zir_index,
- .type_name_ctx = union_type.name,
- };
- defer assert(block_scope.instructions.items.len == 0);
-
- if (body.len != 0) {
- _ = try sema.analyzeInlineBody(&block_scope, body, zir_index);
- }
-
- var int_tag_ty: Type = undefined;
- var enum_field_names: []InternPool.NullTerminatedString = &.{};
- var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty;
- var explicit_tags_seen: []bool = &.{};
- if (tag_type_ref != .none) {
- const tag_ty_src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .{ .node_offset_container_tag = .zero },
- };
- const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
- if (small.auto_enum_tag) {
- // The provided type is an integer type and we must construct the enum tag type here.
- int_tag_ty = provided_ty;
- if (int_tag_ty.zigTypeTag(zcu) != .int and int_tag_ty.zigTypeTag(zcu) != .comptime_int) {
- return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{f}'", .{int_tag_ty.fmt(pt)});
- }
-
- if (fields_len > 0) {
- const field_count_val = try pt.intValue(.comptime_int, fields_len - 1);
- if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
- const msg = msg: {
- const msg = try sema.errMsg(tag_ty_src, "specified integer tag type cannot represent every field", .{});
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(tag_ty_src, msg, "type '{f}' cannot fit values in range 0...{d}", .{
- int_tag_ty.fmt(pt),
- fields_len - 1,
- });
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
- try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
- }
- } else {
- // The provided type is the enum tag type.
- const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) {
- .enum_type => ip.loadEnumType(provided_ty.toIntern()),
- else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}),
- };
- union_type.setTagType(ip, io, provided_ty.toIntern());
- // The fields of the union must match the enum exactly.
- // A flag per field is used to check for missing and extraneous fields.
- explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
- @memset(explicit_tags_seen, false);
- }
- } else {
- // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
- // purposes, we still auto-generate an enum tag type the same way. That the union is
- // untagged is represented by the Type tag (union vs union_tagged).
- enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
- }
-
- var field_types: std.ArrayList(InternPool.Index) = .empty;
- var field_aligns: std.ArrayList(InternPool.Alignment) = .empty;
-
- try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len);
- if (small.any_aligned_fields)
- try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);
-
- var max_bits: u64 = 0;
- var min_bits: u64 = std.math.maxInt(u64);
- var max_bits_src: LazySrcLoc = undefined;
- var min_bits_src: LazySrcLoc = undefined;
- var max_bits_ty: Type = undefined;
- var min_bits_ty: Type = undefined;
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- var bit_bag_index: usize = extra_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- var last_tag_val: ?Value = null;
- const layout = union_type.flagsUnordered(ip).layout;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_type = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const unused = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- _ = unused;
-
- const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
- const field_name_zir = zir.nullTerminatedString(field_name_index);
- extra_index += 1;
-
- const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
- const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- extra_index += 1;
- break :blk field_type_ref;
- } else .none;
-
- const align_ref: Zir.Inst.Ref = if (has_align) blk: {
- const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- extra_index += 1;
- break :blk align_ref;
- } else .none;
-
- const tag_ref: Air.Inst.Ref = if (has_tag) blk: {
- const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- extra_index += 1;
- break :blk try sema.resolveInst(tag_ref);
- } else .none;
-
- const name_src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .{ .container_field_name = field_i },
- };
- const value_src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .{ .container_field_value = field_i },
- };
- const align_src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .{ .container_field_align = field_i },
- };
- const type_src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .{ .container_field_type = field_i },
- };
-
- if (enum_field_vals.capacity() > 0) {
- const enum_tag_val = if (tag_ref != .none) blk: {
- const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, value_src);
- const val = try sema.resolveConstDefinedValue(&block_scope, value_src, coerced, .{ .simple = .enum_field_tag_value });
- last_tag_val = val;
-
- break :blk val;
- } else blk: {
- if (last_tag_val) |last_tag| {
- const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag);
- if (result.overflow) return sema.fail(
- &block_scope,
- value_src,
- "enumeration value '{f}' too large for type '{f}'",
- .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) },
- );
- last_tag_val = result.val;
- } else {
- last_tag_val = try pt.intValue(int_tag_ty, 0);
- }
- break :blk last_tag_val.?;
- };
- const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
- if (gop.found_existing) {
- const other_value_src: LazySrcLoc = .{
- .base_node_inst = union_type.zir_index,
- .offset = .{ .container_field_value = @intCast(gop.index) },
- };
- const msg = msg: {
- const msg = try sema.errMsg(
- value_src,
- "enum tag value {f} already taken",
- .{enum_tag_val.fmtValueSema(pt, sema)},
- );
- errdefer msg.destroy(gpa);
- try sema.errNote(other_value_src, msg, "other occurrence here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- }
-
- // This string needs to outlive the ZIR code.
- const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls);
- if (enum_field_names.len != 0) {
- enum_field_names[field_i] = field_name;
- }
-
- const field_ty: Type = if (!has_type)
- .void
- else if (field_type_ref == .none)
- .noreturn
- else
- try sema.resolveType(&block_scope, type_src, field_type_ref);
-
- if (explicit_tags_seen.len > 0) {
- const tag_ty = union_type.tagTypeUnordered(ip);
- const tag_info = ip.loadEnumType(tag_ty);
- const enum_index = tag_info.nameIndex(ip, field_name) orelse {
- return sema.fail(&block_scope, name_src, "no field named '{f}' in enum '{f}'", .{
- field_name.fmt(ip), Type.fromInterned(tag_ty).fmt(pt),
- });
- };
-
- // No check for duplicate because the check already happened in order
- // to create the enum type in the first place.
- assert(!explicit_tags_seen[enum_index]);
- explicit_tags_seen[enum_index] = true;
-
- // Enforce the enum fields and the union fields being in the same order.
- if (enum_index != field_i) {
- const msg = msg: {
- const enum_field_src: LazySrcLoc = .{
- .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?,
- .offset = .{ .container_field_name = enum_index },
- };
- const msg = try sema.errMsg(name_src, "union field '{f}' ordered differently than corresponding enum field", .{
- field_name.fmt(ip),
- });
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(enum_field_src, msg, "enum field here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- }
-
- if (field_ty.zigTypeTag(zcu) == .@"opaque") {
- const msg = msg: {
- const msg = try sema.errMsg(type_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
- errdefer msg.destroy(sema.gpa);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- switch (layout) {
- .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) {
- const msg = msg: {
- const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- },
- .@"packed" => {
- if (!try sema.validatePackedType(field_ty)) {
- const msg = msg: {
- const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- const field_bits = try field_ty.bitSizeSema(pt);
- if (field_bits >= max_bits) {
- max_bits = field_bits;
- max_bits_src = type_src;
- max_bits_ty = field_ty;
- }
- if (field_bits <= min_bits) {
- min_bits = field_bits;
- min_bits_src = type_src;
- min_bits_ty = field_ty;
- }
- },
- .auto => {},
- }
-
- field_types.appendAssumeCapacity(field_ty.toIntern());
-
- if (small.any_aligned_fields) {
- field_aligns.appendAssumeCapacity(if (align_ref != .none)
- try sema.resolveAlign(&block_scope, align_src, align_ref)
- else
- .none);
- } else {
- assert(align_ref == .none);
- }
- }
-
- union_type.setFieldTypes(ip, field_types.items);
- union_type.setFieldAligns(ip, field_aligns.items);
-
- if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) {
- const msg = msg: {
- const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{});
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits});
- try sema.addDeclaredHereNote(msg, min_bits_ty);
- try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits});
- try sema.addDeclaredHereNote(msg, max_bits_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
-
- if (explicit_tags_seen.len > 0) {
- const tag_ty = union_type.tagTypeUnordered(ip);
- const tag_info = ip.loadEnumType(tag_ty);
- if (tag_info.names.len > fields_len) {
- const msg = msg: {
- const msg = try sema.errMsg(src, "enum field(s) missing in union", .{});
- errdefer msg.destroy(sema.gpa);
-
- for (tag_info.names.get(ip), 0..) |field_name, field_index| {
- if (explicit_tags_seen[field_index]) continue;
- try sema.addFieldErrNote(.fromInterned(tag_ty), field_index, msg, "field '{f}' missing, declared here", .{
- field_name.fmt(ip),
- });
- }
- try sema.addDeclaredHereNote(msg, .fromInterned(tag_ty));
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
- }
- } else if (enum_field_vals.count() > 0) {
- const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name);
- union_type.setTagType(ip, io, enum_ty);
- } else {
- const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name);
- union_type.setTagType(ip, io, enum_ty);
- }
-
- try sema.flushExports();
-}
-
-fn generateUnionTagTypeNumbered(
- sema: *Sema,
- block: *Block,
- enum_field_names: []const InternPool.NullTerminatedString,
- enum_field_vals: []const InternPool.Index,
- union_type: InternPool.Index,
- union_name: InternPool.NullTerminatedString,
-) !InternPool.Index {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const name = try ip.getOrPutStringFmt(
- gpa,
- io,
- pt.tid,
- "@typeInfo({f}).@\"union\".tag_type.?",
- .{union_name.fmt(ip)},
- .no_embedded_nulls,
- );
-
- const enum_ty = try ip.getGeneratedTagEnumType(gpa, io, pt.tid, .{
- .name = name,
- .owner_union_ty = union_type,
- .tag_ty = if (enum_field_vals.len == 0)
- (try pt.intType(.unsigned, 0)).toIntern()
- else
- ip.typeOf(enum_field_vals[0]),
- .names = enum_field_names,
- .values = enum_field_vals,
- .tag_mode = .explicit,
- .parent_namespace = block.namespace,
- });
-
- return enum_ty;
-}
-
-fn generateUnionTagTypeSimple(
- sema: *Sema,
- block: *Block,
- enum_field_names: []const InternPool.NullTerminatedString,
- union_type: InternPool.Index,
- union_name: InternPool.NullTerminatedString,
-) !InternPool.Index {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const name = try ip.getOrPutStringFmt(
- gpa,
- io,
- pt.tid,
- "@typeInfo({f}).@\"union\".tag_type.?",
- .{union_name.fmt(ip)},
- .no_embedded_nulls,
- );
-
- const enum_ty = try ip.getGeneratedTagEnumType(gpa, io, pt.tid, .{
- .name = name,
- .owner_union_ty = union_type,
- .tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(),
- .names = enum_field_names,
- .values = &.{},
- .tag_mode = .auto,
- .parent_namespace = block.namespace,
- });
-
- return enum_ty;
-}
-
-/// There is another implementation of this in `Type.onePossibleValue`. This one
-/// in `Sema` is for calling during semantic analysis, and performs field resolution
-/// to get the answer. The one in `Type` is for calling during codegen and asserts
-/// that the types are already resolved.
-/// TODO assert the return value matches `ty.onePossibleValue`
-pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- return switch (ty.toIntern()) {
- .u0_type,
- .i0_type,
- => try pt.intValue(ty, 0),
- .u1_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u29_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u80_type,
- .u128_type,
- .i128_type,
- .u256_type,
- .usize_type,
- .isize_type,
- .c_char_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f80_type,
- .f128_type,
- .anyopaque_type,
- .bool_type,
- .type_type,
- .anyerror_type,
- .adhoc_inferred_error_set_type,
- .comptime_int_type,
- .comptime_float_type,
- .enum_literal_type,
- .ptr_usize_type,
- .ptr_const_comptime_int_type,
- .manyptr_u8_type,
- .manyptr_const_u8_type,
- .manyptr_const_u8_sentinel_0_type,
- .manyptr_const_slice_const_u8_type,
- .slice_const_u8_type,
- .slice_const_u8_sentinel_0_type,
- .slice_const_slice_const_u8_type,
- .optional_type_type,
- .manyptr_const_type_type,
- .slice_const_type_type,
- .vector_8_i8_type,
- .vector_16_i8_type,
- .vector_32_i8_type,
- .vector_64_i8_type,
- .vector_1_u8_type,
- .vector_2_u8_type,
- .vector_4_u8_type,
- .vector_8_u8_type,
- .vector_16_u8_type,
- .vector_32_u8_type,
- .vector_64_u8_type,
- .vector_2_i16_type,
- .vector_4_i16_type,
- .vector_8_i16_type,
- .vector_16_i16_type,
- .vector_32_i16_type,
- .vector_4_u16_type,
- .vector_8_u16_type,
- .vector_16_u16_type,
- .vector_32_u16_type,
- .vector_2_i32_type,
- .vector_4_i32_type,
- .vector_8_i32_type,
- .vector_16_i32_type,
- .vector_4_u32_type,
- .vector_8_u32_type,
- .vector_16_u32_type,
- .vector_2_i64_type,
- .vector_4_i64_type,
- .vector_8_i64_type,
- .vector_2_u64_type,
- .vector_4_u64_type,
- .vector_8_u64_type,
- .vector_1_u128_type,
- .vector_2_u128_type,
- .vector_1_u256_type,
- .vector_4_f16_type,
- .vector_8_f16_type,
- .vector_16_f16_type,
- .vector_32_f16_type,
- .vector_2_f32_type,
- .vector_4_f32_type,
- .vector_8_f32_type,
- .vector_16_f32_type,
- .vector_2_f64_type,
- .vector_4_f64_type,
- .vector_8_f64_type,
- .anyerror_void_error_union_type,
- => null,
- .void_type => Value.void,
- .noreturn_type => Value.@"unreachable",
- .anyframe_type => unreachable,
- .null_type => Value.null,
- .undefined_type => Value.undef,
- .optional_noreturn_type => try pt.nullValue(ty),
- .generic_poison_type => unreachable,
- .empty_tuple_type => Value.empty_tuple,
- // values, not types
- .undef,
- .undef_bool,
- .undef_usize,
- .undef_u1,
- .zero,
- .zero_usize,
- .zero_u1,
- .zero_u8,
- .one,
- .one_usize,
- .one_u1,
- .one_u8,
- .four_u8,
- .negative_one,
- .void_value,
- .unreachable_value,
- .null_value,
- .bool_true,
- .bool_false,
- .empty_tuple,
- // invalid
- .none,
- => unreachable,
-
- _ => switch (ty.toIntern().unwrap(ip).getTag(ip)) {
- .removed => unreachable,
-
- .type_int_signed, // i0 handled above
- .type_int_unsigned, // u0 handled above
- .type_pointer,
- .type_slice,
- .type_anyframe,
- .type_error_union,
- .type_anyerror_union,
- .type_error_set,
- .type_inferred_error_set,
- .type_opaque,
- .type_function,
- => null,
-
- .simple_type, // handled above
- // values, not types
- .undef,
- .simple_value,
- .ptr_nav,
- .ptr_uav,
- .ptr_uav_aligned,
- .ptr_comptime_alloc,
- .ptr_comptime_field,
- .ptr_int,
- .ptr_eu_payload,
- .ptr_opt_payload,
- .ptr_elem,
- .ptr_field,
- .ptr_slice,
- .opt_payload,
- .opt_null,
- .int_u8,
- .int_u16,
- .int_u32,
- .int_i32,
- .int_usize,
- .int_comptime_int_u32,
- .int_comptime_int_i32,
- .int_small,
- .int_positive,
- .int_negative,
- .int_lazy_align,
- .int_lazy_size,
- .error_set_error,
- .error_union_error,
- .error_union_payload,
- .enum_literal,
- .enum_tag,
- .float_f16,
- .float_f32,
- .float_f64,
- .float_f80,
- .float_f128,
- .float_c_longdouble_f80,
- .float_c_longdouble_f128,
- .float_comptime_float,
- .variable,
- .threadlocal_variable,
- .@"extern",
- .func_decl,
- .func_instance,
- .func_coerced,
- .only_possible_value,
- .union_value,
- .bytes,
- .aggregate,
- .repeated,
- // memoized value, not types
- .memoized_call,
- => unreachable,
-
- .type_array_big,
- .type_array_small,
- .type_vector,
- .type_enum_auto,
- .type_enum_explicit,
- .type_enum_nonexhaustive,
- .type_struct,
- .type_struct_packed,
- .type_struct_packed_inits,
- .type_tuple,
- .type_union,
- => switch (ip.indexToKey(ty.toIntern())) {
- inline .array_type, .vector_type => |seq_type, seq_tag| {
- const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
- if (seq_type.len + @intFromBool(has_sentinel) == 0) return try pt.aggregateValue(ty, &.{});
- if (try sema.typeHasOnePossibleValue(.fromInterned(seq_type.child))) |opv| {
- return try pt.aggregateSplatValue(ty, opv);
- }
- return null;
- },
-
- .struct_type => {
- // Resolving the layout first helps to avoid loops.
- // If the type has a coherent layout, we can recurse through fields safely.
- try ty.resolveLayout(pt);
-
- const struct_type = ip.loadStructType(ty.toIntern());
-
- if (struct_type.field_types.len == 0) {
- // In this case the struct has no fields at all and
- // therefore has one possible value.
- return try pt.aggregateValue(ty, &.{});
- }
-
- const field_vals = try sema.arena.alloc(
- InternPool.Index,
- struct_type.field_types.len,
- );
- for (field_vals, 0..) |*field_val, i| {
- if (struct_type.fieldIsComptime(ip, i)) {
- try ty.resolveStructFieldInits(pt);
- field_val.* = struct_type.field_inits.get(ip)[i];
- continue;
- }
- const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
- if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| {
- field_val.* = field_opv.toIntern();
- } else return null;
- }
-
- // In this case the struct has no runtime-known fields and
- // therefore has one possible value.
- return try pt.aggregateValue(ty, field_vals);
- },
-
- .tuple_type => |tuple| {
- try ty.resolveLayout(pt);
-
- if (tuple.types.len == 0) {
- return try pt.aggregateValue(ty, &.{});
- }
-
- const field_vals = try sema.arena.alloc(
- InternPool.Index,
- tuple.types.len,
- );
- for (
- field_vals,
- tuple.types.get(ip),
- tuple.values.get(ip),
- ) |*field_val, field_ty, field_comptime_val| {
- if (field_comptime_val != .none) {
- field_val.* = field_comptime_val;
- continue;
- }
- if (try sema.typeHasOnePossibleValue(.fromInterned(field_ty))) |opv| {
- field_val.* = opv.toIntern();
- } else return null;
- }
-
- return try pt.aggregateValue(ty, field_vals);
- },
-
- .union_type => {
- // Resolving the layout first helps to avoid loops.
- // If the type has a coherent layout, we can recurse through fields safely.
- try ty.resolveLayout(pt);
-
- const union_obj = ip.loadUnionType(ty.toIntern());
- const tag_val = (try sema.typeHasOnePossibleValue(.fromInterned(union_obj.tagTypeUnordered(ip)))) orelse
- return null;
- if (union_obj.field_types.len == 0) {
- const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() });
- return Value.fromInterned(only);
- }
- const only_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[0]);
- const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse
- return null;
- const only = try pt.internUnion(.{
- .ty = ty.toIntern(),
- .tag = tag_val.toIntern(),
- .val = val_val.toIntern(),
- });
- return Value.fromInterned(only);
- },
-
- .enum_type => {
- const enum_type = ip.loadEnumType(ty.toIntern());
- switch (enum_type.tag_mode) {
- .nonexhaustive => {
- if (enum_type.tag_ty == .comptime_int_type) return null;
-
- if (try sema.typeHasOnePossibleValue(.fromInterned(enum_type.tag_ty))) |int_opv| {
- const only = try pt.intern(.{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = int_opv.toIntern(),
- } });
- return Value.fromInterned(only);
- }
-
- return null;
- },
- .auto, .explicit => {
- if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null;
-
- return Value.fromInterned(switch (enum_type.names.len) {
- 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }),
- 1 => try pt.intern(.{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = if (enum_type.values.len == 0)
- (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
- else
- try ip.getCoercedInts(
- gpa,
- io,
- pt.tid,
- ip.indexToKey(enum_type.values.get(ip)[0]).int,
- enum_type.tag_ty,
- ),
- } }),
- else => return null,
- });
- },
- }
- },
-
- else => unreachable,
- },
-
- .type_optional => {
- const payload_ip = ip.indexToKey(ty.toIntern()).opt_type;
- // Although ?noreturn is handled above, the element type
- // can be effectively noreturn for example via an empty
- // enum or error set.
- if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty);
- return null;
- },
- },
- };
-}
-
/// Returns the type of the AIR instruction.
fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool);
@@ -36235,6 +33562,7 @@ fn isComptimeKnown(
return (try sema.resolveValue(inst)) != null;
}
+/// Asserts that the layout of `var_type` has already been resolved.
fn analyzeComptimeAlloc(
sema: *Sema,
block: *Block,
@@ -36245,10 +33573,9 @@ fn analyzeComptimeAlloc(
const pt = sema.pt;
const zcu = pt.zcu;
- // Needed to make an anon decl with type `var_type` (the `finish()` call below).
- _ = try sema.typeHasOnePossibleValue(var_type);
+ var_type.assertHasLayout(zcu);
- const ptr_type = try pt.ptrTypeSema(.{
+ const ptr_type = try pt.ptrType(.{
.child = var_type.toIntern(),
.flags = .{
.alignment = alignment,
@@ -36256,13 +33583,23 @@ fn analyzeComptimeAlloc(
},
});
- const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment);
-
- return Air.internedToRef((try pt.intern(.{ .ptr = .{
- .ty = ptr_type.toIntern(),
- .base_addr = .{ .comptime_alloc = alloc },
- .byte_offset = 0,
- } })));
+ if (try var_type.onePossibleValue(pt)) |opv| {
+ return .fromIntern(try pt.intern(.{ .ptr = .{
+ .ty = ptr_type.toIntern(),
+ .base_addr = .{ .uav = .{
+ .val = opv.toIntern(),
+ .orig_ty = ptr_type.toIntern(),
+ } },
+ .byte_offset = 0,
+ } }));
+ } else {
+ const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment);
+ return .fromIntern(try pt.intern(.{ .ptr = .{
+ .ty = ptr_type.toIntern(),
+ .base_addr = .{ .comptime_alloc = alloc },
+ .byte_offset = 0,
+ } }));
+ }
}
fn resolveAddressSpace(
@@ -36363,40 +33700,6 @@ fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError
return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int});
}
-/// For pointer-like optionals, it returns the pointer type. For pointers,
-/// the type is returned unmodified.
-/// This can return `error.AnalysisFail` because it sometimes requires resolving whether
-/// a type has zero bits, which can cause a "foo depends on itself" compile error.
-/// This logic must be kept in sync with `Type.isPtrLikeOptional`.
-fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
- const pt = sema.pt;
- const zcu = pt.zcu;
- return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
- .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
- .one, .many, .c => ty,
- .slice => null,
- },
- .opt_type => |opt_child| switch (zcu.intern_pool.indexToKey(opt_child)) {
- .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
- .slice, .c => null,
- .many, .one => {
- if (ptr_type.flags.is_allowzero) return null;
-
- // optionals of zero sized types behave like bools, not pointers
- const payload_ty: Type = .fromInterned(opt_child);
- if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) {
- return null;
- }
-
- return payload_ty;
- },
- },
- else => null,
- },
- else => null,
- };
-}
-
fn unionFieldIndex(
sema: *Sema,
block: *Block,
@@ -36407,9 +33710,9 @@ fn unionFieldIndex(
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- try union_ty.resolveFields(pt);
const union_obj = zcu.typeToUnion(union_ty).?;
- const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse
+ const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
+ const field_index = enum_obj.nameIndex(ip, field_name) orelse
return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name);
return @intCast(field_index);
}
@@ -36424,7 +33727,6 @@ fn structFieldIndex(
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- try struct_ty.resolveFields(pt);
const struct_type = zcu.typeToStruct(struct_ty).?;
return struct_type.nameIndex(ip, field_name) orelse
return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name);
@@ -36513,6 +33815,7 @@ fn intFromFloatScalar(
/// Vectors are also accepted. Vector results are reduced with AND.
///
/// If provided, `vector_index` reports the first element that failed the range check.
+/// MLUGG TODO: move to `Value` or `Type`?
fn intFitsInType(
sema: *Sema,
val: Value,
@@ -36535,30 +33838,10 @@ fn intFitsInType(
.unsigned => info.bits >= ptr_bits,
};
},
- .int => |int| switch (int.storage) {
- .u64, .i64, .big_int => {
- var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
- const big_int = int.storage.toBigInt(&buffer);
- return big_int.fitsInTwosComp(info.signedness, info.bits);
- },
- .lazy_align => |lazy_ty| {
- const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed);
- // If it is u16 or bigger we know the alignment fits without resolving it.
- if (info.bits >= max_needed_bits) return true;
- const x = try Type.fromInterned(lazy_ty).abiAlignmentSema(pt);
- if (x == .none) return true;
- const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed);
- return info.bits >= actual_needed_bits;
- },
- .lazy_size => |lazy_ty| {
- const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed);
- // If it is u64 or bigger we know the size fits without resolving it.
- if (info.bits >= max_needed_bits) return true;
- const x = try Type.fromInterned(lazy_ty).abiSizeSema(pt);
- if (x == 0) return true;
- const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
- return info.bits >= actual_needed_bits;
- },
+ .int => |int| {
+ var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
+ const big_int = int.storage.toBigInt(&buffer);
+ return big_int.fitsInTwosComp(info.signedness, info.bits);
},
.aggregate => |aggregate| {
assert(ty.zigTypeTag(zcu) == .vector);
@@ -36588,23 +33871,23 @@ fn intFitsInType(
fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
const pt = sema.pt;
- if (!(try int_val.compareAllWithZeroSema(.gte, pt))) return false;
+ if (!int_val.compareAllWithZero(.gte, pt.zcu)) return false;
const end_val = try pt.intValue(tag_ty, end);
if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
return true;
}
-/// Asserts the type is an enum.
+/// Asserts the type is an exhaustive enum.
fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
const pt = sema.pt;
const zcu = pt.zcu;
const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern());
- assert(enum_type.tag_mode != .nonexhaustive);
+ assert(!enum_type.nonexhaustive);
// The `tagValueIndex` function call below relies on the type being the integer tag type.
// `getCoerced` assumes the value will fit the new type.
- if (!(try sema.intFitsInType(int, .fromInterned(enum_type.tag_ty), null))) return false;
- const int_coerced = try pt.getCoerced(int, .fromInterned(enum_type.tag_ty));
-
+ const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type);
+ if (!try sema.intFitsInType(int, int_tag_ty, null)) return false;
+ const int_coerced = try pt.getCoerced(int, int_tag_ty);
return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;
}
@@ -36636,6 +33919,7 @@ fn compareAll(
}
/// Asserts the values are comparable. Both operands have type `ty`.
+/// MLUGG TODO: move to `Value`?
fn compareScalar(
sema: *Sema,
lhs: Value,
@@ -36644,17 +33928,19 @@ fn compareScalar(
ty: Type,
) CompileError!bool {
const pt = sema.pt;
+ const zcu = pt.zcu;
+
const coerced_lhs = try pt.getCoerced(lhs, ty);
const coerced_rhs = try pt.getCoerced(rhs, ty);
// Equality comparisons of signed zero and NaN need to use floating point semantics
- if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu))
- return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt);
+ if (coerced_lhs.isFloat(zcu) or coerced_rhs.isFloat(zcu))
+ return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu);
switch (op) {
- .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty),
- .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)),
- else => return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt),
+ .eq => return Value.eql(coerced_lhs, coerced_rhs, ty, zcu),
+ .neq => return !Value.eql(coerced_lhs, coerced_rhs, ty, zcu),
+ else => return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu),
}
}
@@ -36799,7 +34085,7 @@ fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Ai
});
}
-fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError {
+pub fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError {
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value});
errdefer msg.destroy(sema.gpa);
@@ -36867,11 +34153,7 @@ fn notePathToComptimeAllocPtr(
else => {}, // there will be another stage
}
- const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.Canceled => @panic("TODO"), // pls don't be cancelable mlugg
- error.AnalysisFail => unreachable,
- };
+ const derivation = try comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema);
var second_path_aw: std.Io.Writer.Allocating = .init(arena);
defer second_path_aw.deinit();
@@ -37058,12 +34340,12 @@ fn maybeDerefSliceAsArray(
else => unreachable,
};
const elem_ty = Type.fromInterned(slice.ty).childType(zcu);
- const len = try Value.fromInterned(slice.len).toUnsignedIntSema(pt);
+ const len = Value.fromInterned(slice.len).toUnsignedInt(zcu);
const array_ty = try pt.arrayType(.{
.child = elem_ty.toIntern(),
.len = len,
});
- const ptr_ty = try pt.ptrTypeSema(p: {
+ const ptr_ty = try pt.ptrType(p: {
var p = Type.fromInterned(slice.ty).ptrInfo(zcu);
p.flags.size = .one;
p.child = array_ty.toIntern();
@@ -37129,238 +34411,6 @@ pub fn flushExports(sema: *Sema) !void {
}
}
-/// Called as soon as a `declared` enum type is created.
-/// Resolves the tag type and field inits.
-/// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this.
-pub fn resolveDeclaredEnum(
- pt: Zcu.PerThread,
- wip_ty: InternPool.WipEnumType,
- inst: Zir.Inst.Index,
- tracked_inst: InternPool.TrackedInst.Index,
- namespace: InternPool.NamespaceIndex,
- type_name: InternPool.NullTerminatedString,
- small: Zir.Inst.EnumDecl.Small,
- body: []const Zir.Inst.Index,
- tag_type_ref: Zir.Inst.Ref,
- any_values: bool,
- fields_len: u32,
- zir: Zir,
- body_end: usize,
-) Zcu.SemaError!void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
-
- var arena: std.heap.ArenaAllocator = .init(gpa);
- defer arena.deinit();
-
- var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
- defer comptime_err_ret_trace.deinit();
-
- var sema: Sema = .{
- .pt = pt,
- .gpa = gpa,
- .arena = arena.allocator(),
- .code = zir,
- .owner = .wrap(.{ .type = wip_ty.index }),
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = .void,
- .fn_ret_ty_ies = null,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
-
- if (zcu.comp.debugIncremental()) {
- const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner);
- info.last_update_gen = zcu.generation;
- }
-
- try sema.declareDependency(.{ .src_hash = tracked_inst });
-
- var block: Block = .{
- .parent = null,
- .sema = &sema,
- .namespace = namespace,
- .instructions = .{},
- .inlining = null,
- .comptime_reason = .{ .reason = .{
- .src = src,
- .r = .{ .simple = .enum_field_values },
- } },
- .src_base_inst = tracked_inst,
- .type_name_ctx = type_name,
- };
- defer block.instructions.deinit(gpa);
-
- sema.resolveDeclaredEnumInner(
- &block,
- wip_ty,
- inst,
- tracked_inst,
- src,
- small,
- body,
- tag_type_ref,
- any_values,
- fields_len,
- zir,
- body_end,
- ) catch |err| switch (err) {
- error.ComptimeBreak => unreachable,
- error.ComptimeReturn => unreachable,
- error.OutOfMemory, error.Canceled => |e| return e,
- error.AnalysisFail => {
- if (!zcu.failed_analysis.contains(sema.owner)) {
- try zcu.transitive_failed_analysis.put(gpa, sema.owner, {});
- }
- return error.AnalysisFail;
- },
- };
-}
-
-fn resolveDeclaredEnumInner(
- sema: *Sema,
- block: *Block,
- wip_ty: InternPool.WipEnumType,
- inst: Zir.Inst.Index,
- tracked_inst: InternPool.TrackedInst.Index,
- src: LazySrcLoc,
- small: Zir.Inst.EnumDecl.Small,
- body: []const Zir.Inst.Index,
- tag_type_ref: Zir.Inst.Ref,
- any_values: bool,
- fields_len: u32,
- zir: Zir,
- body_end: usize,
-) Zcu.CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
-
- const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = .zero } };
-
- const int_tag_ty = ty: {
- if (body.len != 0) {
- _ = try sema.analyzeInlineBody(block, body, inst);
- }
-
- if (tag_type_ref != .none) {
- const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
- if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) {
- return sema.fail(block, tag_ty_src, "expected integer tag type, found '{f}'", .{ty.fmt(pt)});
- }
- break :ty ty;
- } else if (fields_len == 0) {
- break :ty try pt.intType(.unsigned, 0);
- } else {
- const bits = std.math.log2_int_ceil(usize, fields_len);
- break :ty try pt.intType(.unsigned, bits);
- }
- };
-
- wip_ty.setTagTy(ip, int_tag_ty.toIntern());
-
- var extra_index = body_end + bit_bags_count;
- var bit_bag_index: usize = body_end;
- var cur_bit_bag: u32 = undefined;
- var last_tag_val: ?Value = null;
- for (0..fields_len) |field_i_usize| {
- const field_i: u32 = @intCast(field_i_usize);
- if (field_i % 32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
-
- const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
- const field_name_zir = zir.nullTerminatedString(field_name_index);
- extra_index += 1; // field name
-
- const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls);
-
- const value_src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_value = field_i },
- };
-
- const tag_overflow = if (has_tag_value) overflow: {
- const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- extra_index += 1;
- const tag_inst = try sema.resolveInst(tag_val_ref);
- last_tag_val = try sema.resolveConstDefinedValue(block, .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_name = field_i },
- }, tag_inst, .{ .simple = .enum_field_tag_value });
- if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
- last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
- if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
- assert(conflict.kind == .value); // AstGen validated names are unique
- const other_field_src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_value = conflict.prev_field_idx },
- };
- const msg = msg: {
- const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
- errdefer msg.destroy(gpa);
- try sema.errNote(other_field_src, msg, "other occurrence here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- break :overflow false;
- } else if (any_values) overflow: {
- if (last_tag_val) |last_tag| {
- const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag);
- last_tag_val = result.val;
- if (result.overflow) break :overflow true;
- } else {
- last_tag_val = try pt.intValue(int_tag_ty, 0);
- }
- if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
- assert(conflict.kind == .value); // AstGen validated names are unique
- const other_field_src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_value = conflict.prev_field_idx },
- };
- const msg = msg: {
- const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
- errdefer msg.destroy(gpa);
- try sema.errNote(other_field_src, msg, "other occurrence here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- break :overflow false;
- } else overflow: {
- assert(wip_ty.nextField(ip, field_name, .none) == null);
- last_tag_val = try pt.intValue(.comptime_int, field_i);
- if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
- last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
- break :overflow false;
- };
-
- if (tag_overflow) {
- const msg = try sema.errMsg(value_src, "enumeration value '{f}' too large for type '{f}'", .{
- last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt),
- });
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- }
- if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
- if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {
- return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
- }
- }
-}
-
pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;
pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;
@@ -37369,6 +34419,11 @@ const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadR
const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr;
const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult;
+// MLUGG TODO: decide how to do the namespacing here
+pub const type_resolution = @import("Sema/type_resolution.zig");
+pub const ensureLayoutResolved = type_resolution.ensureLayoutResolved;
+pub const ensureFieldInitsResolved = type_resolution.ensureFieldInitsResolved;
+
pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type {
assert(decl.kind() == .type);
try sema.ensureMemoizedStateResolved(src, decl.stage());
@@ -37483,11 +34538,11 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc,
const result = try sema.analyzeNavVal(block, src, nav);
const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null);
- const maybe_lazy_val: Value = switch (builtin_decl.kind()) {
+ const val: Value = switch (builtin_decl.kind()) {
.type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) {
return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name });
} else val: {
- try uncoerced_val.toType().resolveFully(pt);
+ try sema.ensureLayoutResolved(uncoerced_val.toType());
break :val uncoerced_val;
},
.func => val: {
@@ -37500,7 +34555,6 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc,
break :val .fromInterned(coerced.toInterned().?);
},
};
- const val = try sema.resolveLazyValue(maybe_lazy_val);
const prev = zcu.builtin_decl_values.get(builtin_decl);
if (val.toIntern() != prev) {
@@ -37539,7 +34593,6 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ
=> try pt.funcType(.{
.param_types = &.{ .generic_poison_type, .generic_poison_type },
.return_type = .noreturn_type,
- .is_generic = true,
}),
// `fn (anyerror) noreturn`
@@ -37590,3 +34643,823 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ
else => unreachable,
};
}
+
+/// TODO MLUGG: this is a gnarly hack
+const PartialTypeName = union(enum) {
+ exact: struct {
+ name: InternPool.NullTerminatedString,
+ nav: InternPool.Nav.Index.Optional,
+ },
+ anon_prefix: []const u8,
+ fn apply(
+ name: PartialTypeName,
+ wip: *const InternPool.WipContainerType,
+ pt: Zcu.PerThread,
+ ) (Allocator.Error || std.Io.Cancelable)!InternPool.NullTerminatedString {
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const ip = &zcu.intern_pool;
+ switch (name) {
+ .exact => |e| {
+ wip.setName(ip, e.name, e.nav);
+ return e.name;
+ },
+ .anon_prefix => |prefix| {
+ const resolved_name = try ip.getOrPutStringFmt(
+ comp.gpa,
+ comp.io,
+ pt.tid,
+ "{s}_{d}",
+ .{ prefix, @intFromEnum(wip.index) },
+ .no_embedded_nulls,
+ );
+ wip.setName(ip, resolved_name, .none);
+ return resolved_name;
+ },
+ }
+ }
+};
+pub fn createTypeName(
+ sema: *Sema,
+ block: *Block,
+ name_strategy: Zir.Inst.NameStrategy,
+ anon_prefix: []const u8,
+ inst: Zir.Inst.Index,
+) CompileError!PartialTypeName {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ switch (name_strategy) {
+ .anon => {}, // handled after switch
+ .parent => return .{ .exact = .{
+ .name = block.type_name_ctx,
+ .nav = sema.owner.unwrap().nav_val.toOptional(),
+ } },
+ .func => func_strat: {
+ const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail);
+ const zir_tags = sema.code.instructions.items(.tag);
+
+ var aw: std.Io.Writer.Allocating = .init(gpa);
+ defer aw.deinit();
+ const w = &aw.writer;
+ w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
+
+ var arg_i: usize = 0;
+ for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
+ .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
+ const arg = sema.inst_map.get(zir_inst).?;
+ // If this is being called in a generic function then analyzeCall will
+ // have already resolved the args and this will work.
+ // If not then this is a struct type being returned from a non-generic
+ // function and the name doesn't matter since it will later
+ // result in a compile error.
+ const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat
+
+ if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory;
+
+ // Limiting the depth here helps avoid type names getting too long, which
+ // in turn helps to avoid unreasonably long symbol names for namespaced
+ // symbols. Such names should ideally be human-readable, and additionally,
+ // some tooling may not support very long symbol names.
+ w.print("{f}", .{Value.fmtValueSemaFull(.{
+ .val = arg_val,
+ .pt = pt,
+ .opt_sema = sema,
+ .depth = 1,
+ })}) catch return error.OutOfMemory;
+
+ arg_i += 1;
+ continue;
+ },
+ else => continue,
+ };
+
+ w.writeByte(')') catch return error.OutOfMemory;
+ return .{ .exact = .{
+ .name = try ip.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls),
+ .nav = .none,
+ } };
+ },
+ .dbg_var => {
+ // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions.
+ const ref = inst.toRef();
+ const zir_tags = sema.code.instructions.items(.tag);
+ const zir_data = sema.code.instructions.items(.data);
+ for (@intFromEnum(inst)..zir_tags.len) |i| switch (zir_tags[i]) {
+ .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) {
+ return .{ .exact = .{
+ .name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{
+ block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code),
+ }, .no_embedded_nulls),
+ .nav = .none,
+ } };
+ },
+ else => {},
+ };
+ // fall through to anon strat
+ },
+ }
+
+ // anon strat handling
+
+ // It would be neat to have "struct:line:column" but this name has
+ // to survive incremental updates, where it may have been shifted down
+ // or up to a different line, but unchanged, and thus not unnecessarily
+ // semantically analyzed.
+ // TODO: that would be possible, by detecting line number changes and renaming
+ // types appropriately. However, `@typeName` becomes a problem then. If we remove
+ // that builtin from the language, we can consider this.
+
+ return .{ .anon_prefix = try std.fmt.allocPrint(
+ sema.arena,
+ "{f}__{s}",
+ .{ block.type_name_ctx.fmt(ip), anon_prefix },
+ ) };
+}
+
+pub fn analyzeStructDecl(
+ pt: Zcu.PerThread,
+ file_index: Zcu.File.Index,
+ zir: *const Zir,
+ parent_namespace: InternPool.OptionalNamespaceIndex,
+ tracked_inst: InternPool.TrackedInst.Index,
+ struct_decl: *const Zir.UnwrappedStructDecl,
+ explicit_backing_type: ?Type,
+ captures: []const InternPool.CaptureValue,
+ type_name: PartialTypeName,
+) (Allocator.Error || std.Io.Cancelable)!Type {
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ const wip = switch (try ip.getStructType(gpa, io, pt.tid, .{
+ .fields_len = @intCast(struct_decl.field_names.len),
+ .layout = struct_decl.layout,
+ .explicit_packed_backing_type = if (explicit_backing_type) |ty| ty.toIntern() else .none,
+ .any_comptime_fields = struct_decl.field_comptime_bits != null,
+ .any_field_defaults = struct_decl.field_default_body_lens != null,
+ .any_field_aligns = struct_decl.field_align_body_lens != null,
+ .key = .{ .declared = .{
+ .zir_index = tracked_inst,
+ .captures = captures,
+ } },
+ })) {
+ .existing => |ty| return .fromInterned(ty),
+ .wip => |wip| wip,
+ };
+ errdefer wip.cancel(ip, pt.tid);
+
+ _ = try type_name.apply(&wip, pt);
+
+ var field_it = struct_decl.iterateFields();
+ while (field_it.next()) |field| {
+ const name_slice = zir.nullTerminatedString(field.name);
+ const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
+ assert(wip.nextField(ip, name, field.is_comptime) == null); // AstGen validated this for us
+ }
+
+ const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
+ .parent = parent_namespace,
+ .owner_type = wip.index,
+ .file_scope = file_index,
+ .generation = zcu.generation,
+ });
+ errdefer pt.destroyNamespace(new_namespace_index);
+
+ try pt.scanNamespace(new_namespace_index, struct_decl.decls);
+
+ // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) });
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_inits = wip.index }) });
+
+ if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
+
+ try zcu.outdated.ensureUnusedCapacity(gpa, 2);
+ try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2);
+ errdefer comptime unreachable; // because we don't remove the `outdated` entries
+ zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0);
+ zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_inits = wip.index }), 0);
+ zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {});
+ zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_inits = wip.index }), {});
+
+ return .fromInterned(wip.finish(ip, new_namespace_index));
+}
+const AnalyzeUnionDeclError = error{
+ OutOfMemory,
+ Canceled,
+ /// `packed union(T)` syntax was used, but `T` was not an integer type.
+ ExplicitBackingNotInt,
+ /// `union(enum(T))` syntax was used, but `T` was not an integer type.
+ ExplicitTagNotInt,
+ /// `union(T)` syntax was used, but `T` was not an enum type.
+ ExplicitTagNotEnum,
+ /// `union(T)` syntax was used, but the fields of the union do not exactly
+ /// correspond to the fields of the enum `T`.
+ ExplicitTagFieldMismatch,
+};
+fn analyzeUnionDecl(
+ pt: Zcu.PerThread,
+ file_index: Zcu.File.Index,
+ zir: *const Zir,
+ parent_namespace: InternPool.OptionalNamespaceIndex,
+ want_safe_types: bool,
+ tracked_inst: InternPool.TrackedInst.Index,
+ union_decl: *const Zir.UnwrappedUnionDecl,
+ arg_type: ?Type,
+ captures: []const InternPool.CaptureValue,
+ type_name: PartialTypeName,
+) AnalyzeUnionDeclError!Type {
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ switch (union_decl.kind) {
+ .tagged_explicit => if (arg_type.?.zigTypeTag(zcu) != .@"enum") {
+ return error.ExplicitTagNotEnum;
+ },
+ .tagged_enum_explicit => if (arg_type.?.zigTypeTag(zcu) != .int) {
+ return error.ExplicitTagNotInt;
+ },
+ .packed_explicit => if (arg_type.?.zigTypeTag(zcu) != .int) {
+ return error.ExplicitBackingNotInt;
+ },
+ .auto,
+ .tagged_enum,
+ .@"extern",
+ .@"packed",
+ => assert(arg_type == null),
+ }
+
+ const wip = switch (try ip.getUnionType(gpa, io, pt.tid, .{
+ .fields_len = @intCast(union_decl.field_names.len),
+ .layout = union_decl.kind.layout(),
+ .explicit_packed_backing_type = switch (union_decl.kind) {
+ .packed_explicit => arg_type.?.toIntern(),
+ else => .none,
+ },
+ .runtime_tag = switch (union_decl.kind) {
+ .auto => if (want_safe_types) .safety else .none,
+
+ .tagged_explicit,
+ .tagged_enum,
+ .tagged_enum_explicit,
+ => .tagged,
+
+ .@"extern",
+ .@"packed",
+ .packed_explicit,
+ => .none,
+ },
+ .have_explicit_enum_tag = union_decl.kind == .tagged_explicit,
+ .any_field_aligns = union_decl.field_align_body_lens != null,
+ .key = .{ .declared = .{
+ .zir_index = tracked_inst,
+ .captures = captures,
+ .arg_ty = if (arg_type) |t| t.toIntern() else .none,
+ } },
+ })) {
+ .existing => |ty| return .fromInterned(ty),
+ .wip => |wip| wip,
+ };
+ errdefer wip.cancel(ip, pt.tid);
+
+ const resolved_type_name = try type_name.apply(&wip, pt);
+
+ const generated_tag_ty: InternPool.Index = if (union_decl.kind == .tagged_explicit) generated_tag_ty: {
+ const tag_type = arg_type.?;
+ const enum_field_names = ip.loadEnumType(tag_type.toIntern()).field_names;
+ // Check that the enum field names match the union field names
+ if (union_decl.field_names.len != enum_field_names.len) {
+ return error.ExplicitTagFieldMismatch;
+ }
+ for (union_decl.field_names, enum_field_names.get(ip)) |union_field_zir, enum_field_ip| {
+ const union_field_name = zir.nullTerminatedString(union_field_zir);
+ const enum_field_name = enum_field_ip.toSlice(ip);
+ if (!std.mem.eql(u8, union_field_name, enum_field_name)) {
+ return error.ExplicitTagFieldMismatch;
+ }
+ }
+ wip.setTagType(ip, tag_type.toIntern());
+ break :generated_tag_ty .none;
+ } else generated_tag_ty: {
+ // Generate a tag type. Even if the union is untagged (`.none`), we still generate a
+ // hypothetical tag type.
+ const wip_tag_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
+ .fields_len = @intCast(union_decl.field_names.len),
+ .explicit_int_tag_type = switch (union_decl.kind) {
+ .tagged_enum_explicit => arg_type.?.toIntern(),
+ else => .none,
+ },
+ .nonexhaustive = false,
+ .key = .{ .generated_union_tag = wip.index },
+ })) {
+ .existing => unreachable, // enum type is keyed on this union type which we're only just creating
+ .wip => |wip_tag_ty| wip_tag_ty,
+ };
+ errdefer wip_tag_ty.cancel(ip, pt.tid);
+ // Populate the generated tag type's name
+ const tag_type_name = try ip.getOrPutStringFmt(
+ gpa,
+ io,
+ pt.tid,
+ "@typeInfo({f}).@\"union\".tag_type.?",
+ .{resolved_type_name.fmt(ip)},
+ .no_embedded_nulls,
+ );
+ wip_tag_ty.setName(ip, tag_type_name, .none);
+ // Populate the generated tag type's field names
+ for (union_decl.field_names) |zir_name| {
+ const name_slice = zir.nullTerminatedString(zir_name);
+ const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
+ assert(wip_tag_ty.nextField(ip, name, false) == null); // AstGen validated this for us
+ }
+ // If not explicitly given, populate the generated tag type's *integer* tag type
+ switch (union_decl.kind) {
+ .tagged_enum_explicit => {}, // already set by `getEnumType`
+ else => {
+ // Infer the int tag type from the field count
+ const bits = Type.smallestUnsignedBits(union_decl.field_names.len -| 1);
+ const int_tag_type = try pt.intType(.unsigned, bits);
+ wip_tag_ty.setTagType(ip, int_tag_type.toIntern());
+ },
+ }
+ // Create a dummy namespace for the generated tag type
+ const new_namespace_index = try pt.createNamespace(.{
+ .parent = parent_namespace,
+ .owner_type = wip_tag_ty.index,
+ .file_scope = file_index,
+ .generation = zcu.generation,
+ });
+ errdefer pt.destroyNamespace(new_namespace_index);
+ wip.setTagType(ip, wip_tag_ty.index);
+ break :generated_tag_ty wip_tag_ty.finish(ip, new_namespace_index);
+ };
+ // If we fail to create the union type, we must delete the generated enum tag type, since it
+ // would hold a reference to the deleted union.
+ errdefer if (generated_tag_ty != .none) ip.remove(pt.tid, generated_tag_ty);
+
+ const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
+ .parent = parent_namespace,
+ .owner_type = wip.index,
+ .file_scope = file_index,
+ .generation = zcu.generation,
+ });
+ errdefer pt.destroyNamespace(new_namespace_index);
+
+ try pt.scanNamespace(new_namespace_index, union_decl.decls);
+
+ // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) });
+ if (generated_tag_ty != .none) {
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_inits = generated_tag_ty }) });
+ }
+
+ if (zcu.comp.debugIncremental()) {
+ try zcu.incremental_debug_state.newType(zcu, wip.index);
+ if (generated_tag_ty != .none) {
+ try zcu.incremental_debug_state.newType(zcu, generated_tag_ty);
+ }
+ }
+
+ try zcu.outdated.ensureUnusedCapacity(gpa, 2);
+ try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2);
+ errdefer comptime unreachable; // because we don't remove the `outdated` entry
+ zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0);
+ zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {});
+ if (generated_tag_ty != .none) {
+ zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_inits = generated_tag_ty }), 0);
+ zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_inits = generated_tag_ty }), {});
+ }
+
+ return .fromInterned(wip.finish(ip, new_namespace_index));
+}
+const AnalyzeEnumDeclError = error{
+ OutOfMemory,
+ Canceled,
+ /// `enum(T)` syntax was used, but `T` was not an integer type.
+ ExplicitTagNotInt,
+};
+fn analyzeEnumDecl(
+ pt: Zcu.PerThread,
+ file_index: Zcu.File.Index,
+ zir: *const Zir,
+ parent_namespace: InternPool.OptionalNamespaceIndex,
+ tracked_inst: InternPool.TrackedInst.Index,
+ enum_decl: *const Zir.UnwrappedEnumDecl,
+ explicit_tag_type: ?Type,
+ captures: []const InternPool.CaptureValue,
+ type_name: PartialTypeName,
+) AnalyzeEnumDeclError!Type {
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ if (explicit_tag_type) |ty| {
+ // MLUGG TODO: make a final call on whether comptime_int is a valid int tag type, and follow it everywhere.
+ // i think not in the name of simplicity, but my opinion might depend on whether it's broken in practice today
+ switch (ty.zigTypeTag(zcu)) {
+ .int, .comptime_int => {},
+ else => return error.ExplicitTagNotInt,
+ }
+ }
+
+ const wip = switch (try ip.getEnumType(gpa, io, pt.tid, .{
+ .fields_len = @intCast(enum_decl.field_names.len),
+ .explicit_int_tag_type = if (explicit_tag_type) |ty| ty.toIntern() else .none,
+ .nonexhaustive = enum_decl.nonexhaustive,
+ .key = .{ .declared = .{
+ .zir_index = tracked_inst,
+ .captures = captures,
+ } },
+ })) {
+ .existing => |ty| return .fromInterned(ty),
+ .wip => |wip| wip,
+ };
+ errdefer wip.cancel(ip, pt.tid);
+
+ _ = try type_name.apply(&wip, pt);
+
+ var field_it = enum_decl.iterateFields();
+ while (field_it.next()) |field| {
+ const name_slice = zir.nullTerminatedString(field.name);
+ const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
+ assert(wip.nextField(ip, name, false) == null); // AstGen validated this for us
+ }
+
+ if (explicit_tag_type == null) {
+ // Infer the int tag type from the field count
+ const bits = Type.smallestUnsignedBits(enum_decl.field_names.len -| 1);
+ const int_tag_ty = try pt.intType(.unsigned, bits);
+ wip.setTagType(ip, int_tag_ty.toIntern());
+ }
+
+ const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
+ .parent = parent_namespace,
+ .owner_type = wip.index,
+ .file_scope = file_index,
+ .generation = zcu.generation,
+ });
+ errdefer pt.destroyNamespace(new_namespace_index);
+
+ try pt.scanNamespace(new_namespace_index, enum_decl.decls);
+
+ // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_inits = wip.index }) });
+
+ if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
+
+ try zcu.outdated.ensureUnusedCapacity(gpa, 1);
+ try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
+ errdefer comptime unreachable; // because we don't remove the `outdated` entry
+ zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_inits = wip.index }), 0);
+ zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_inits = wip.index }), {});
+
+ return .fromInterned(wip.finish(ip, new_namespace_index));
+}
+fn analyzeOpaqueDecl(
+ pt: Zcu.PerThread,
+ file_index: Zcu.File.Index,
+ parent_namespace: InternPool.OptionalNamespaceIndex,
+ tracked_inst: InternPool.TrackedInst.Index,
+ opaque_decl: *const Zir.UnwrappedOpaqueDecl,
+ captures: []const InternPool.CaptureValue,
+ type_name: PartialTypeName,
+) (Allocator.Error || std.Io.Cancelable)!Type {
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ const wip = switch (try ip.getOpaqueType(gpa, io, pt.tid, .{
+ .zir_index = tracked_inst,
+ .captures = captures,
+ })) {
+ .existing => |ty| return .fromInterned(ty),
+ .wip => |wip| wip,
+ };
+ errdefer wip.cancel(ip, pt.tid);
+
+ _ = try type_name.apply(&wip, pt);
+
+ const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
+ .parent = parent_namespace,
+ .owner_type = wip.index,
+ .file_scope = file_index,
+ .generation = zcu.generation,
+ });
+ errdefer pt.destroyNamespace(new_namespace_index);
+
+ try pt.scanNamespace(new_namespace_index, opaque_decl.decls);
+
+ if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
+ return .fromInterned(wip.finish(ip, new_namespace_index));
+}
+
+fn zirStructDecl(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+
+ const tracked_inst = try block.trackZir(inst);
+
+ const src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .nodeOffset(.zero),
+ };
+ const backing_ty_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .node_offset_container_tag = .zero },
+ };
+
+ const struct_decl = sema.code.getStructDecl(inst);
+
+ const captures = try sema.getCaptures(block, src, struct_decl.captures, struct_decl.capture_names);
+
+ const backing_int_type: ?Type = ty: {
+ if (struct_decl.backing_int_type == .none) break :ty null;
+ break :ty try sema.resolveType(block, backing_ty_src, struct_decl.backing_int_type);
+ // MLUGG TODO validate it's an int!
+ };
+
+ const ty = try analyzeStructDecl(
+ pt,
+ block.getFileScopeIndex(zcu),
+ &sema.code,
+ block.namespace.toOptional(),
+ tracked_inst,
+ &struct_decl,
+ backing_int_type,
+ captures,
+ try sema.createTypeName(block, struct_decl.name_strategy, "struct", inst),
+ );
+
+ try sema.addTypeReferenceEntry(src, ty);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ // TODO MLUGG: me no likey, maybe model namespaces less badly idk
+ try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu));
+
+ return .fromIntern(ty.toIntern());
+}
+fn zirUnionDecl(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ const tracked_inst = try block.trackZir(inst);
+
+ const src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .nodeOffset(.zero),
+ };
+ const arg_ty_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .node_offset_container_tag = .zero },
+ };
+
+ const union_decl = sema.code.getUnionDecl(inst);
+
+ const captures = try sema.getCaptures(block, src, union_decl.captures, union_decl.capture_names);
+
+ const arg_type: ?Type = ty: {
+ if (union_decl.arg_type == .none) break :ty null;
+ break :ty try sema.resolveType(block, arg_ty_src, union_decl.arg_type);
+ };
+
+ const ty = analyzeUnionDecl(
+ pt,
+ block.getFileScopeIndex(zcu),
+ &sema.code,
+ block.namespace.toOptional(),
+ block.wantSafeTypes(),
+ tracked_inst,
+ &union_decl,
+ arg_type,
+ captures,
+ try sema.createTypeName(block, union_decl.name_strategy, "union", inst),
+ ) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+
+ error.ExplicitBackingNotInt => return sema.fail(
+ block,
+ arg_ty_src,
+ "expected integer backing type, found '{f}'",
+ .{arg_type.?.fmt(pt)},
+ ),
+ error.ExplicitTagNotInt => return sema.fail(
+ block,
+ arg_ty_src,
+ "expected integer tag type, found '{f}'",
+ .{arg_type.?.fmt(pt)},
+ ),
+ error.ExplicitTagNotEnum => return sema.fail(
+ block,
+ arg_ty_src,
+ "expected enum tag type, found '{f}'",
+ .{arg_type.?.fmt(pt)},
+ ),
+ error.ExplicitTagFieldMismatch => {
+ const enum_obj = ip.loadEnumType(arg_type.?.toIntern());
+ const enum_to_union_map = try sema.arena.alloc(?u32, enum_obj.field_names.len);
+ @memset(enum_to_union_map, null);
+ for (union_decl.field_names, 0..) |field_name_zir, union_field_idx| {
+ const field_name_ip = try ip.getOrPutString(gpa, io, pt.tid, sema.code.nullTerminatedString(field_name_zir), .no_embedded_nulls);
+ if (enum_obj.nameIndex(ip, field_name_ip)) |enum_field_idx| {
+ enum_to_union_map[enum_field_idx] = @intCast(union_field_idx);
+ continue;
+ }
+ const union_field_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_name = @intCast(union_field_idx) },
+ };
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(union_field_src, "no field named '{f}' in enum '{f}'", .{ field_name_ip.fmt(ip), arg_type.?.fmt(pt) });
+ errdefer msg.destroy(gpa);
+ try sema.addDeclaredHereNote(msg, arg_type.?);
+ break :msg msg;
+ });
+ }
+ for (enum_to_union_map, 0..) |union_field_idx, enum_field_idx| {
+ if (union_field_idx != null) continue;
+ const field_name_ip = enum_obj.field_names.get(ip)[enum_field_idx];
+ const enum_field_src: LazySrcLoc = .{
+ .base_node_inst = arg_type.?.typeDeclInstAllowGeneratedTag(zcu).?,
+ .offset = .{ .container_field_name = @intCast(enum_field_idx) },
+ };
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(src, "enum field '{f}' missing from union", .{field_name_ip.fmt(ip)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(enum_field_src, msg, "enum field here", .{});
+ break :msg msg;
+ });
+ }
+ for (enum_to_union_map, 0..) |union_field_idx, enum_field_idx| {
+ if (union_field_idx.? == enum_field_idx) continue;
+ const field_name = sema.code.nullTerminatedString(
+ union_decl.field_names[union_field_idx.?],
+ );
+ const union_field_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_name = union_field_idx.? },
+ };
+ const enum_field_src: LazySrcLoc = .{
+ .base_node_inst = arg_type.?.typeDeclInstAllowGeneratedTag(zcu).?,
+ .offset = .{ .container_field_name = @intCast(enum_field_idx) },
+ };
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(src, "union field order does not match tag enum field order", .{});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(union_field_src, msg, "union field '{s}' is index {d}", .{ field_name, union_field_idx.? });
+ try sema.errNote(enum_field_src, msg, "enum field '{s}' is index {d}", .{ field_name, enum_field_idx });
+ break :msg msg;
+ });
+ }
+ unreachable;
+ },
+ };
+
+ const enum_tag_ty = ty.unionTagTypeHypothetical(zcu);
+ switch (ip.indexToKey(enum_tag_ty.toIntern()).enum_type) {
+ .declared, .reified => {},
+ .generated_union_tag => |owner_union_ty| {
+ assert(owner_union_ty == ty.toIntern());
+ // generated tag type [MLUGG]
+ // Enum inits are resolved eagerly. TODO MLUGG: honestly i don't think they SHOULD be lol
+ try sema.ensureFieldInitsResolved(.fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_type));
+ },
+ }
+
+ try sema.addTypeReferenceEntry(src, ty);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ // TODO MLUGG: me no likey, maybe model namespaces less badly idk
+ try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu));
+
+ return .fromIntern(ty.toIntern());
+}
+fn zirEnumDecl(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+
+ const tracked_inst = try block.trackZir(inst);
+
+ const src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .nodeOffset(.zero),
+ };
+ const tag_ty_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .node_offset_container_tag = .zero },
+ };
+
+ const enum_decl = sema.code.getEnumDecl(inst);
+
+ const captures = try sema.getCaptures(block, src, enum_decl.captures, enum_decl.capture_names);
+
+ const tag_type: ?Type = ty: {
+ if (enum_decl.tag_type == .none) break :ty null;
+ break :ty try sema.resolveType(block, tag_ty_src, enum_decl.tag_type);
+ };
+
+ const ty = analyzeEnumDecl(
+ pt,
+ block.getFileScopeIndex(zcu),
+ &sema.code,
+ block.namespace.toOptional(),
+ tracked_inst,
+ &enum_decl,
+ tag_type,
+ captures,
+ try sema.createTypeName(block, enum_decl.name_strategy, "enum", inst),
+ ) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+
+ error.ExplicitTagNotInt => return sema.fail(
+ block,
+ tag_ty_src,
+ "expected integer tag type, found '{f}'",
+ .{tag_type.?.fmt(pt)},
+ ),
+ };
+
+ // Enum inits are resolved eagerly. TODO MLUGG: honestly i don't think they SHOULD be lol
+ try sema.ensureFieldInitsResolved(ty);
+
+ try sema.addTypeReferenceEntry(src, ty);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ // TODO MLUGG: me no likey, maybe model namespaces less badly idk
+ try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu));
+
+ return .fromIntern(ty.toIntern());
+}
+fn zirOpaqueDecl(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+
+ const tracked_inst = try block.trackZir(inst);
+
+ const src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .nodeOffset(.zero),
+ };
+
+ const opaque_decl = sema.code.getOpaqueDecl(inst);
+
+ const captures = try sema.getCaptures(block, src, opaque_decl.captures, opaque_decl.capture_names);
+
+ const ty = try analyzeOpaqueDecl(
+ pt,
+ block.getFileScopeIndex(zcu),
+ block.namespace.toOptional(),
+ tracked_inst,
+ &opaque_decl,
+ captures,
+ try sema.createTypeName(block, opaque_decl.name_strategy, "opaque", inst),
+ );
+
+ try sema.addTypeReferenceEntry(src, ty);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ // TODO MLUGG: me no likey, maybe model namespaces less badly idk
+ try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu));
+
+ return .fromIntern(ty.toIntern());
+}
diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig
index 76cf3d7f2cc05310e4402cc3fe43072d2a07fc59..78d1d8d1df28af4b827e6d869d0e8352acd168ee 100644
--- a/src/Sema/LowerZon.zig
+++ b/src/Sema/LowerZon.zig
@@ -125,6 +125,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter
return (try pt.aggregateValue(.fromInterned(ty), values)).toIntern();
},
.struct_literal => |init| {
+ if (true) @panic("MLUGG TODO");
const elems = try self.sema.arena.alloc(InternPool.Index, init.names.len);
for (0..init.names.len) |i| {
elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i)));
@@ -299,7 +300,7 @@ fn checkTypeInner(
} else {
const gop = try visited.getOrPut(sema.arena, ty.toIntern());
if (gop.found_existing) return;
- try ty.resolveFields(pt);
+ try sema.ensureLayoutResolved(ty);
const struct_info = zcu.typeToStruct(ty).?;
for (struct_info.field_types.get(ip)) |field_type| {
try self.checkTypeInner(.fromInterned(field_type), null, visited);
@@ -308,7 +309,7 @@ fn checkTypeInner(
.@"union" => {
const gop = try visited.getOrPut(sema.arena, ty.toIntern());
if (gop.found_existing) return;
- try ty.resolveFields(pt);
+ try sema.ensureLayoutResolved(ty);
const union_info = zcu.typeToUnion(ty).?;
for (union_info.field_types.get(ip)) |field_type| {
if (field_type != .void_type) {
@@ -767,8 +768,8 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
const io = comp.io;
const ip = &pt.zcu.intern_pool;
- try res_ty.resolveFields(self.sema.pt);
- try res_ty.resolveStructFieldInits(self.sema.pt);
+ try self.sema.ensureLayoutResolved(res_ty);
+ try self.sema.ensureFieldInitsResolved(res_ty);
const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
@@ -779,7 +780,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len);
- const field_defaults = struct_info.field_inits.get(ip);
+ const field_defaults = struct_info.field_defaults.get(ip);
if (field_defaults.len > 0) {
@memcpy(field_values, field_defaults);
} else {
@@ -803,7 +804,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]);
field_values[name_index] = try self.lowerExprKnownResTy(field_node, field_type);
- if (struct_info.comptime_bits.getBit(ip, name_index)) {
+ if (struct_info.field_is_comptime_bits.get(ip, name_index)) {
const val = ip.indexToKey(field_values[name_index]);
const default = ip.indexToKey(field_defaults[name_index]);
if (!val.eql(default, ip)) {
@@ -918,9 +919,9 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
- try res_ty.resolveFields(self.sema.pt);
- const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?;
- const enum_tag_info = union_info.loadTagType(ip);
+ try self.sema.ensureLayoutResolved(res_ty);
+ const union_info = pt.zcu.typeToUnion(res_ty).?;
+ const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type);
const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) {
.enum_literal => |name| b: {
@@ -956,7 +957,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
const name_index = enum_tag_info.nameIndex(ip, field_name) orelse {
return error.WrongType;
};
- const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_ty), name_index);
+ const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_type), name_index);
const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
const val = if (maybe_field_node) |field_node| b: {
if (field_type.toIntern() == .void_type) {
diff --git a/src/Sema/arith.zig b/src/Sema/arith.zig
index c646dc7b2167450ba54779bf2d83af6e0de86cc1..161b6e1ce03ad4ffd9ee8372f49fb39a8a279207 100644
--- a/src/Sema/arith.zig
+++ b/src/Sema/arith.zig
@@ -1053,7 +1053,7 @@ fn shlScalar(
if (rhs_val.isUndef(zcu)) return rhs_val;
},
}
- switch (try rhs_val.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {},
.eq => return lhs_val,
.lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
@@ -1090,7 +1090,7 @@ fn shlWithOverflowScalar(
if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
- switch (try rhs_val.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {},
.eq => return .{ .overflow_bit = .zero_u1, .wrapped_result = lhs_val },
.lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
@@ -1169,7 +1169,7 @@ fn shrScalar(
if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
- switch (try rhs_val.orderAgainstZeroSema(pt)) {
+ switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
.gt => {},
.eq => return lhs_val,
.lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
@@ -1430,8 +1430,8 @@ fn intAddWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value
const info = ty.intInfo(zcu);
var lhs_space: Value.BigIntSpace = undefined;
var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
- const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt);
+ const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
+ const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
const limbs = try sema.arena.alloc(
std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(info.bits),
@@ -1512,8 +1512,8 @@ fn intSubWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value
const info = ty.intInfo(zcu);
var lhs_space: Value.BigIntSpace = undefined;
var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
- const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt);
+ const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
+ const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
const limbs = try sema.arena.alloc(
std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(info.bits),
@@ -1597,8 +1597,8 @@ fn intMulWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value
const info = ty.intInfo(zcu);
var lhs_space: Value.BigIntSpace = undefined;
var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
- const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt);
+ const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
+ const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
const limbs = try sema.arena.alloc(
std.math.big.Limb,
lhs_bigint.limbs.len + rhs_bigint.limbs.len,
@@ -1840,7 +1840,7 @@ fn intShl(
var lhs_space: Value.BigIntSpace = undefined;
const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
- const shift_amt: usize = @intCast(try rhs.toUnsignedIntSema(pt));
+ const shift_amt: usize = @intCast(rhs.toUnsignedInt(zcu));
if (shift_amt >= info.bits) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
}
@@ -1862,7 +1862,7 @@ fn intShlSat(
const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
const shift_amt: usize = amt: {
- if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| {
+ if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| {
if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt;
}
// We only support ints with up to 2^16 - 1 bits, so this
@@ -1895,9 +1895,9 @@ fn intShlWithOverflow(
const info = lhs_ty.intInfo(zcu);
var lhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
+ const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
- const shift_amt: usize = @intCast(try rhs.toUnsignedIntSema(pt));
+ const shift_amt: usize = @intCast(rhs.toUnsignedInt(zcu));
if (shift_amt >= info.bits) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
}
@@ -1924,9 +1924,10 @@ fn comptimeIntShl(
vec_idx: ?usize,
) !Value {
const pt = sema.pt;
+ const zcu = pt.zcu;
var lhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
- if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| {
+ const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
+ if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| {
if (std.math.cast(usize, shift_amt_u64)) |shift_amt| {
const result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
return pt.intValue_big(.comptime_int, result_bigint.toConst());
@@ -1963,15 +1964,15 @@ fn intShr(
const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
const shift_amt: usize = if (rhs_ty.toIntern() == .comptime_int_type) amt: {
- if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| {
+ if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| {
if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt;
}
- if (try rhs.compareAllWithZeroSema(.lt, pt)) {
+ if (rhs.compareAllWithZero(.lt, zcu)) {
return sema.failWithNegativeShiftAmount(block, rhs_src, rhs, vec_idx);
} else {
return sema.failWithUnsupportedComptimeShiftAmount(block, rhs_src, vec_idx);
}
- } else @intCast(try rhs.toUnsignedIntSema(pt));
+ } else @intCast(rhs.toUnsignedInt(zcu));
if (lhs_ty.toIntern() != .comptime_int_type and shift_amt >= lhs_ty.intInfo(zcu).bits) {
return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
@@ -2006,7 +2007,7 @@ fn intBitReverse(sema: *Sema, val: Value, ty: Type) !Value {
const info = ty.intInfo(zcu);
var val_space: Value.BigIntSpace = undefined;
- const val_bigint = try val.toBigIntSema(&val_space, pt);
+ const val_bigint = val.toBigInt(&val_space, zcu);
const limbs = try sema.arena.alloc(
std.math.big.Limb,
diff --git a/src/Sema/bitcast.zig b/src/Sema/bitcast.zig
index bc1859e51b4a60d58d628efef3c0158fc295aa6e..ee70bd1746466aaf57c8812083f37892e69dac59 100644
--- a/src/Sema/bitcast.zig
+++ b/src/Sema/bitcast.zig
@@ -79,8 +79,8 @@ fn bitCastInner(
const val_ty = val.typeOf(zcu);
- try val_ty.resolveLayout(pt);
- try dest_ty.resolveLayout(pt);
+ val_ty.assertHasLayout(zcu);
+ try sema.ensureLayoutResolved(dest_ty);
assert(val_ty.hasWellDefinedLayout(zcu));
@@ -138,8 +138,8 @@ fn bitCastSpliceInner(
const val_ty = val.typeOf(zcu);
const splice_val_ty = splice_val.typeOf(zcu);
- try val_ty.resolveLayout(pt);
- try splice_val_ty.resolveLayout(pt);
+ try sema.ensureLayoutResolved(val_ty);
+ try sema.ensureLayoutResolved(splice_val_ty);
const splice_bits = splice_val_ty.bitSize(zcu);
@@ -673,6 +673,9 @@ const PackValueBits = struct {
fn primitive(pack: *PackValueBits, want_ty: Type) BitCastError!Value {
const pt = pack.pt;
const zcu = pt.zcu;
+
+ if (try want_ty.onePossibleValue(pt)) |opv| return opv;
+
const vals, const bit_offset = pack.prepareBits(want_ty.bitSize(zcu));
for (vals) |val| {
diff --git a/src/Sema/comptime_ptr_access.zig b/src/Sema/comptime_ptr_access.zig
index 4e101ecd0f962fa49a2aa73ac228ce6c6fe7e3c0..c74b5c2d75fab499c27a973584d3aa0748515a94 100644
--- a/src/Sema/comptime_ptr_access.zig
+++ b/src/Sema/comptime_ptr_access.zig
@@ -67,7 +67,7 @@ pub fn storeComptimePtr(
{
const store_ty: Type = .fromInterned(ptr_info.child);
- if (!try store_ty.comptimeOnlySema(pt) and !try store_ty.hasRuntimeBitsIgnoreComptimeSema(pt)) {
+ if (!store_ty.comptimeOnly(zcu) and !store_ty.hasRuntimeBits(zcu)) {
// zero-bit store; nothing to do
return .success;
}
@@ -354,8 +354,8 @@ fn loadComptimePtrInner(
const load_one_ty, const load_count = load_ty.arrayBase(zcu);
const extra_base_index: u64 = if (ptr.byte_offset == 0) 0 else idx: {
- if (try load_one_ty.comptimeOnlySema(pt)) break :restructure_array;
- const elem_len = try load_one_ty.abiSizeSema(pt);
+ if (load_one_ty.comptimeOnly(zcu)) break :restructure_array;
+ const elem_len = load_one_ty.abiSize(zcu);
if (ptr.byte_offset % elem_len != 0) break :restructure_array;
break :idx @divExact(ptr.byte_offset, elem_len);
};
@@ -401,12 +401,12 @@ fn loadComptimePtrInner(
var cur_offset = ptr.byte_offset;
if (load_ty.zigTypeTag(zcu) == .array and array_offset > 0) {
- cur_offset += try load_ty.childType(zcu).abiSizeSema(pt) * array_offset;
+ cur_offset += load_ty.childType(zcu).abiSize(zcu) * array_offset;
}
- const need_bytes = if (host_bits > 0) (host_bits + 7) / 8 else try load_ty.abiSizeSema(pt);
+ const need_bytes = if (host_bits > 0) (host_bits + 7) / 8 else load_ty.abiSize(zcu);
- if (cur_offset + need_bytes > try cur_val.typeOf(zcu).abiSizeSema(pt)) {
+ if (cur_offset + need_bytes > cur_val.typeOf(zcu).abiSize(zcu)) {
return .{ .out_of_bounds = cur_val.typeOf(zcu) };
}
@@ -441,7 +441,7 @@ fn loadComptimePtrInner(
.optional => break, // this can only be a pointer-like optional so is terminal
.array => {
const elem_ty = cur_ty.childType(zcu);
- const elem_size = try elem_ty.abiSizeSema(pt);
+ const elem_size = elem_ty.abiSize(zcu);
const elem_idx = cur_offset / elem_size;
const next_elem_off = elem_size * (elem_idx + 1);
if (cur_offset + need_bytes <= next_elem_off) {
@@ -457,7 +457,7 @@ fn loadComptimePtrInner(
.@"packed" => break, // let the bitcast logic handle this
.@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| {
const start_off = cur_ty.structFieldOffset(field_idx, zcu);
- const end_off = start_off + try cur_ty.fieldType(field_idx, zcu).abiSizeSema(pt);
+ const end_off = start_off + cur_ty.fieldType(field_idx, zcu).abiSize(zcu);
if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) {
cur_val = try cur_val.getElem(sema.pt, field_idx);
cur_offset -= start_off;
@@ -484,7 +484,7 @@ fn loadComptimePtrInner(
};
// The payload always has offset 0. If it's big enough
// to represent the whole load type, we can use it.
- if (try payload.typeOf(zcu).abiSizeSema(pt) >= need_bytes) {
+ if (payload.typeOf(zcu).abiSize(zcu) >= need_bytes) {
cur_val = payload;
} else {
break;
@@ -753,8 +753,8 @@ fn prepareComptimePtrStore(
const store_one_ty, const store_count = store_ty.arrayBase(zcu);
const extra_base_index: u64 = if (ptr.byte_offset == 0) 0 else idx: {
- if (try store_one_ty.comptimeOnlySema(pt)) break :restructure_array;
- const elem_len = try store_one_ty.abiSizeSema(pt);
+ if (store_one_ty.comptimeOnly(zcu)) break :restructure_array;
+ const elem_len = store_one_ty.abiSize(zcu);
if (ptr.byte_offset % elem_len != 0) break :restructure_array;
break :idx @divExact(ptr.byte_offset, elem_len);
};
@@ -807,11 +807,11 @@ fn prepareComptimePtrStore(
var cur_val: *MutableValue, var cur_offset: u64 = switch (base_strat) {
.direct => |direct| .{ direct.val, 0 },
// It's okay to do `abiSize` - the comptime-only case will be caught below.
- .index => |index| .{ index.val, index.elem_index * try index.val.typeOf(zcu).childType(zcu).abiSizeSema(pt) },
+ .index => |index| .{ index.val, index.elem_index * index.val.typeOf(zcu).childType(zcu).abiSize(zcu) },
.flat_index => |flat_index| .{
flat_index.val,
// It's okay to do `abiSize` - the comptime-only case will be caught below.
- flat_index.flat_elem_index * try flat_index.val.typeOf(zcu).arrayBase(zcu)[0].abiSizeSema(pt),
+ flat_index.flat_elem_index * flat_index.val.typeOf(zcu).arrayBase(zcu)[0].abiSize(zcu),
},
.reinterpret => |r| .{ r.val, r.byte_offset },
else => unreachable,
@@ -823,12 +823,12 @@ fn prepareComptimePtrStore(
}
if (store_ty.zigTypeTag(zcu) == .array and array_offset > 0) {
- cur_offset += try store_ty.childType(zcu).abiSizeSema(pt) * array_offset;
+ cur_offset += store_ty.childType(zcu).abiSize(zcu) * array_offset;
}
- const need_bytes = try store_ty.abiSizeSema(pt);
+ const need_bytes = store_ty.abiSize(zcu);
- if (cur_offset + need_bytes > try cur_val.typeOf(zcu).abiSizeSema(pt)) {
+ if (cur_offset + need_bytes > cur_val.typeOf(zcu).abiSize(zcu)) {
return .{ .out_of_bounds = cur_val.typeOf(zcu) };
}
@@ -863,7 +863,7 @@ fn prepareComptimePtrStore(
.optional => break, // this can only be a pointer-like optional so is terminal
.array => {
const elem_ty = cur_ty.childType(zcu);
- const elem_size = try elem_ty.abiSizeSema(pt);
+ const elem_size = elem_ty.abiSize(zcu);
const elem_idx = cur_offset / elem_size;
const next_elem_off = elem_size * (elem_idx + 1);
if (cur_offset + need_bytes <= next_elem_off) {
@@ -879,7 +879,7 @@ fn prepareComptimePtrStore(
.@"packed" => break, // let the bitcast logic handle this
.@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| {
const start_off = cur_ty.structFieldOffset(field_idx, zcu);
- const end_off = start_off + try cur_ty.fieldType(field_idx, zcu).abiSizeSema(pt);
+ const end_off = start_off + cur_ty.fieldType(field_idx, zcu).abiSize(zcu);
if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) {
cur_val = try cur_val.elem(pt, sema.arena, field_idx);
cur_offset -= start_off;
@@ -902,7 +902,7 @@ fn prepareComptimePtrStore(
};
// The payload always has offset 0. If it's big enough
// to represent the whole load type, we can use it.
- if (try payload.typeOf(zcu).abiSizeSema(pt) >= need_bytes) {
+ if (payload.typeOf(zcu).abiSize(zcu) >= need_bytes) {
cur_val = payload;
} else {
break;
diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig
new file mode 100644
index 0000000000000000000000000000000000000000..f54a7b944e41f3c39161836a49654b177abb7c99
--- /dev/null
+++ b/src/Sema/type_resolution.zig
@@ -0,0 +1,993 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
+
+const Sema = @import("../Sema.zig");
+const Block = Sema.Block;
+const Type = @import("../Type.zig");
+const Value = @import("../Value.zig");
+const Zcu = @import("../Zcu.zig");
+const CompileError = Zcu.CompileError;
+const SemaError = Zcu.SemaError;
+const LazySrcLoc = Zcu.LazySrcLoc;
+const InternPool = @import("../InternPool.zig");
+const Alignment = InternPool.Alignment;
+const arith = @import("arith.zig");
+
+/// Ensures that `ty` has known layout, including alignment, size, and (where relevant) field offsets.
+/// `ty` may be any type; its layout is resolved *recursively* if necessary.
+/// Adds incremental dependencies tracking any required type resolution.
+/// MLUGG TODO: to make the langspec non-stupid, we need to call this from WAY fewer places (the conditions need to be less specific).
+/// e.g. I think creating the type `fn (A, B) C` should force layout resolution of `A`,`B`,`C`, which will simplify some `analyzeCall` logic.
+/// wait i just realised that's probably a terrible idea, fns are a common cause of dep loops rn... so maybe not lol idk...
+/// perhaps "layout resolution" for a function should resolve layout of ret ty and stuff, idk. justification: the "layout" of a function is whether
+/// fnHasRuntimeBits, which depends whether the ret ty is comptime-only, i.e. the ret ty layout
+/// MLUGG TODO: to be clear, i should audit EVERY use of this before PRing
+pub fn ensureLayoutResolved(sema: *Sema, ty: Type) SemaError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ switch (ip.indexToKey(ty.toIntern())) {
+ .int_type,
+ .ptr_type,
+ .anyframe_type,
+ .simple_type,
+ .opaque_type,
+ .enum_type,
+ .error_set_type,
+ .inferred_error_set_type,
+ => {},
+
+ .func_type => |func_type| {
+ for (func_type.param_types.get(ip)) |param_ty| {
+ try ensureLayoutResolved(sema, .fromInterned(param_ty));
+ }
+ try ensureLayoutResolved(sema, .fromInterned(func_type.return_type));
+ },
+
+ .array_type => |arr| return ensureLayoutResolved(sema, .fromInterned(arr.child)),
+ .vector_type => |vec| return ensureLayoutResolved(sema, .fromInterned(vec.child)),
+ .opt_type => |child| return ensureLayoutResolved(sema, .fromInterned(child)),
+ .error_union_type => |eu| return ensureLayoutResolved(sema, .fromInterned(eu.payload_type)),
+ .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| {
+ try ensureLayoutResolved(sema, .fromInterned(field_ty));
+ },
+ .struct_type, .union_type => {
+ try sema.declareDependency(.{ .type_layout = ty.toIntern() });
+ if (zcu.analysis_in_progress.contains(.wrap(.{ .type_layout = ty.toIntern() }))) {
+ // TODO: better error message
+ return sema.failWithOwnedErrorMsg(null, try sema.errMsg(
+ ty.srcLoc(zcu),
+ "{s} '{f}' depends on itself",
+ .{ @tagName(ty.zigTypeTag(zcu)), ty.fmt(pt) },
+ ));
+ }
+ try pt.ensureTypeLayoutUpToDate(ty);
+ },
+
+ // values, not types
+ .undef,
+ .simple_value,
+ .variable,
+ .@"extern",
+ .func,
+ .int,
+ .err,
+ .error_union,
+ .enum_literal,
+ .enum_tag,
+ .empty_enum_value,
+ .float,
+ .ptr,
+ .slice,
+ .opt,
+ .aggregate,
+ .un,
+ // memoization, not types
+ .memoized_call,
+ => unreachable,
+ }
+}
+
+/// Asserts that `ty` is either a `struct` type, or an `enum` type.
+/// If `ty` is a struct, ensures that fields' default values are resolved.
+/// If `ty` is an enum, ensures that fields' integer tag valus are resolved.
+/// Adds incremental dependencies tracking the required type resolution.
+pub fn ensureFieldInitsResolved(sema: *Sema, ty: Type) SemaError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ switch (ip.indexToKey(ty.toIntern())) {
+ .struct_type, .enum_type => {},
+ else => unreachable, // assertion failure
+ }
+
+ try sema.declareDependency(.{ .type_inits = ty.toIntern() });
+ if (zcu.analysis_in_progress.contains(.wrap(.{ .type_inits = ty.toIntern() }))) {
+ // TODO: better error message
+ return sema.failWithOwnedErrorMsg(null, try sema.errMsg(
+ ty.srcLoc(zcu),
+ "{s} '{f}' depends on itself",
+ .{ @tagName(ty.zigTypeTag(zcu)), ty.fmt(pt) },
+ ));
+ }
+ try pt.ensureTypeInitsUpToDate(ty);
+}
+/// Asserts that `struct_ty` is a non-packed non-tuple struct, and that `sema.owner` is that type.
+/// This function *does* register the `src_hash` dependency on the struct.
+pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ assert(sema.owner.unwrap().type_layout == struct_ty.toIntern());
+
+ const struct_obj = ip.loadStructType(struct_ty.toIntern());
+ const zir_index = struct_obj.zir_index.resolve(ip).?;
+
+ assert(struct_obj.layout != .@"packed");
+
+ try sema.declareDependency(.{ .src_hash = struct_obj.zir_index });
+
+ var block: Block = .{
+ .parent = null,
+ .sema = sema,
+ .namespace = struct_obj.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .comptime_reason = undefined, // always set before using `block`
+ .src_base_inst = struct_obj.zir_index,
+ .type_name_ctx = struct_obj.name,
+ };
+ defer assert(block.instructions.items.len == 0);
+
+ const zir_struct = sema.code.getStructDecl(zir_index);
+ var field_it = zir_struct.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const field_ty_src: LazySrcLoc = .{
+ .base_node_inst = struct_obj.zir_index,
+ .offset = .{ .container_field_type = zir_field.idx },
+ };
+ const field_align_src: LazySrcLoc = .{
+ .base_node_inst = struct_obj.zir_index,
+ .offset = .{ .container_field_align = zir_field.idx },
+ };
+
+ const field_ty: Type = field_ty: {
+ block.comptime_reason = .{ .reason = .{
+ .src = field_ty_src,
+ .r = .{ .simple = .struct_field_types },
+ } };
+ const type_ref = try sema.resolveInlineBody(&block, zir_field.type_body, zir_index);
+ break :field_ty try sema.analyzeAsType(&block, field_ty_src, type_ref);
+ };
+ assert(!field_ty.isGenericPoison());
+
+ try sema.ensureLayoutResolved(field_ty);
+
+ const explicit_field_align: Alignment = a: {
+ block.comptime_reason = .{ .reason = .{
+ .src = field_align_src,
+ .r = .{ .simple = .struct_field_attrs },
+ } };
+ const align_body = zir_field.align_body orelse break :a .none;
+ const align_ref = try sema.resolveInlineBody(&block, align_body, zir_index);
+ break :a try sema.analyzeAsAlign(&block, field_align_src, align_ref);
+ };
+
+ if (field_ty.zigTypeTag(zcu) == .@"opaque") {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ if (struct_obj.layout == .@"extern" and !try sema.validateExternType(field_ty, .struct_field)) {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.explainWhyTypeIsNotExtern(msg, field_ty_src, field_ty, .struct_field);
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+
+ struct_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
+ if (struct_obj.field_aligns.len != 0) {
+ struct_obj.field_aligns.get(ip)[zir_field.idx] = explicit_field_align;
+ } else {
+ assert(explicit_field_align == .none);
+ }
+ }
+
+ try finishStructLayout(sema, &block, struct_ty.srcLoc(zcu), struct_ty.toIntern(), &struct_obj);
+}
+
+/// Called after populating field types and alignments; populates field offsets, runtime order, and
+/// overall struct layout information (size, alignment, comptime-only state, etc).
+pub fn finishStructLayout(
+ sema: *Sema,
+ /// Only used to report compile errors.
+ block: *Block,
+ struct_src: LazySrcLoc,
+ struct_ty: InternPool.Index,
+ struct_obj: *const InternPool.LoadedStructType,
+) SemaError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+ var comptime_only = false;
+ var one_possible_value = true;
+ var struct_align: Alignment = .@"1";
+ // Unlike `struct_obj.field_aligns`, these are not `.none`.
+ const resolved_field_aligns = try sema.arena.alloc(Alignment, struct_obj.field_names.len);
+ for (resolved_field_aligns, 0..) |*align_out, field_idx| {
+ const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);
+ const field_align: Alignment = a: {
+ if (struct_obj.field_aligns.len != 0) {
+ const a = struct_obj.field_aligns.get(ip)[field_idx];
+ if (a != .none) break :a a;
+ }
+ break :a field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu);
+ };
+ if (!struct_obj.field_is_comptime_bits.get(ip, field_idx)) {
+ // Non-`comptime` fields contribute to the struct's layout.
+ struct_align = struct_align.maxStrict(field_align);
+ if (field_ty.comptimeOnly(zcu)) comptime_only = true;
+ if (try field_ty.onePossibleValue(pt) == null) one_possible_value = false;
+ if (struct_obj.layout == .auto) {
+ struct_obj.field_runtime_order.get(ip)[field_idx] = @enumFromInt(field_idx);
+ }
+ } else if (struct_obj.layout == .auto) {
+ struct_obj.field_runtime_order.get(ip)[field_idx] = .omitted; // comptime fields are not in the runtime order
+ }
+ align_out.* = field_align;
+ }
+ if (struct_obj.layout == .auto) {
+ const runtime_order = struct_obj.field_runtime_order.get(ip);
+ // This logic does not reorder fields; it only moves the omitted ones to the end so that logic
+ // elsewhere does not need to special-case. TODO: support field reordering in all the backends!
+ if (!zcu.backendSupportsFeature(.field_reordering)) {
+ var i: usize = 0;
+ var off: usize = 0;
+ while (i + off < runtime_order.len) {
+ if (runtime_order[i + off] == .omitted) {
+ off += 1;
+ } else {
+ runtime_order[i] = runtime_order[i + off];
+ i += 1;
+ }
+ }
+ } else {
+ // Sort by descending alignment to minimize padding.
+ const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder;
+ const AlignSortCtx = struct {
+ aligns: []const Alignment,
+ fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool {
+ assert(a != .unresolved);
+ assert(b != .unresolved);
+ if (a == .omitted) return false;
+ if (b == .omitted) return true;
+ const a_align = ctx.aligns[@intFromEnum(a)];
+ const b_align = ctx.aligns[@intFromEnum(b)];
+ return a_align.compare(.gt, b_align);
+ }
+ };
+ mem.sortUnstable(
+ RuntimeOrder,
+ runtime_order,
+ @as(AlignSortCtx, .{ .aligns = resolved_field_aligns }),
+ AlignSortCtx.lessThan,
+ );
+ }
+ }
+
+ var runtime_order_it = struct_obj.iterateRuntimeOrder(ip);
+ var cur_offset: u64 = 0;
+ while (runtime_order_it.next()) |field_idx| {
+ const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);
+ const offset = resolved_field_aligns[field_idx].forward(cur_offset);
+ struct_obj.field_offsets.get(ip)[field_idx] = @truncate(offset); // truncate because the overflow is handled below
+ cur_offset = offset + field_ty.abiSize(zcu);
+ }
+ const struct_size = std.math.cast(u32, struct_align.forward(cur_offset)) orelse return sema.fail(
+ block,
+ struct_src,
+ "struct layout requires size {d}, this compiler implementation supports up to {d}",
+ .{ struct_align.forward(cur_offset), std.math.maxInt(u32) },
+ );
+ ip.resolveStructLayout(
+ io,
+ struct_ty,
+ struct_size,
+ struct_align,
+ false, // MLUGG TODO XXX NPV
+ one_possible_value,
+ comptime_only,
+ );
+}
+
+/// Asserts that `struct_ty` is a packed struct, and that `sema.owner` is that type.
+/// This function *does* register the `src_hash` dependency on the struct.
+pub fn resolvePackedStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ assert(sema.owner.unwrap().type_layout == struct_ty.toIntern());
+
+ const struct_obj = ip.loadStructType(struct_ty.toIntern());
+ const zir_index = struct_obj.zir_index.resolve(ip).?;
+
+ assert(struct_obj.layout == .@"packed");
+
+ try sema.declareDependency(.{ .src_hash = struct_obj.zir_index });
+
+ var block: Block = .{
+ .parent = null,
+ .sema = sema,
+ .namespace = struct_obj.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .comptime_reason = undefined, // always set before using `block`
+ .src_base_inst = struct_obj.zir_index,
+ .type_name_ctx = struct_obj.name,
+ };
+ defer assert(block.instructions.items.len == 0);
+
+ var field_bits: u64 = 0;
+ const zir_struct = sema.code.getStructDecl(zir_index);
+ var field_it = zir_struct.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const field_ty_src: LazySrcLoc = .{
+ .base_node_inst = struct_obj.zir_index,
+ .offset = .{ .container_field_type = zir_field.idx },
+ };
+ const field_ty: Type = field_ty: {
+ block.comptime_reason = .{ .reason = .{
+ .src = field_ty_src,
+ .r = .{ .simple = .struct_field_types },
+ } };
+ const type_ref = try sema.resolveInlineBody(&block, zir_field.type_body, zir_index);
+ break :field_ty try sema.analyzeAsType(&block, field_ty_src, type_ref);
+ };
+ assert(!field_ty.isGenericPoison());
+ struct_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
+
+ try sema.ensureLayoutResolved(field_ty);
+
+ if (field_ty.zigTypeTag(zcu) == .@"opaque") {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ if (!field_ty.packable(zcu)) {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.explainWhyTypeIsNotPackable(msg, field_ty_src, field_ty);
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ assert(!field_ty.comptimeOnly(zcu)); // packable types are not comptime-only
+ field_bits += field_ty.bitSize(zcu);
+ }
+
+ try resolvePackedStructBackingInt(sema, &block, field_bits, struct_ty, &struct_obj);
+}
+
+pub fn resolvePackedStructBackingInt(
+ sema: *Sema,
+ block: *Block,
+ field_bits: u64,
+ struct_ty: Type,
+ struct_obj: *const InternPool.LoadedStructType,
+) SemaError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ switch (struct_obj.packed_backing_mode) {
+ .explicit => {
+ // We only need to validate the type.
+ const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
+ assert(backing_ty.zigTypeTag(zcu) == .int);
+ if (field_bits != backing_ty.intInfo(zcu).bits) return sema.failWithOwnedErrorMsg(block, msg: {
+ const src = struct_ty.srcLoc(zcu);
+ const msg = try sema.errMsg(src, "backing integer bit width does not match total bit width of fields", .{});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(src, msg, "backing integer '{f}' has bit width '{d}'", .{ backing_ty.fmt(pt), backing_ty.bitSize(zcu) });
+ try sema.errNote(src, msg, "struct fields have total bit width '{d}'", .{field_bits});
+ break :msg msg;
+ });
+ },
+ .auto => {
+ // We need to generate the inferred tag.
+ const want_bits = std.math.cast(u16, field_bits) orelse return sema.fail(
+ block,
+ struct_ty.srcLoc(zcu),
+ "packed struct bit width '{d}' exceeds maximum bit width of 65535",
+ .{field_bits},
+ );
+ const backing_int = try pt.intType(.unsigned, want_bits);
+ ip.resolvePackedStructBackingInt(io, struct_ty.toIntern(), backing_int.toIntern());
+ },
+ }
+}
+
+/// Asserts that `struct_ty` is a non-tuple struct, and that `sema.owner` is that type.
+/// This function *does* register the `src_hash` dependency on the struct.
+pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ assert(sema.owner.unwrap().type_inits == struct_ty.toIntern());
+
+ try sema.ensureLayoutResolved(struct_ty);
+
+ const struct_obj = ip.loadStructType(struct_ty.toIntern());
+ const zir_index = struct_obj.zir_index.resolve(ip).?;
+
+ try sema.declareDependency(.{ .src_hash = struct_obj.zir_index });
+
+ if (struct_obj.field_defaults.len == 0) {
+ // The struct has no default field values, so the slice has been omitted.
+ return;
+ }
+
+ const field_types = struct_obj.field_types.get(ip);
+
+ var block: Block = .{
+ .parent = null,
+ .sema = sema,
+ .namespace = struct_obj.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .comptime_reason = undefined, // always set before using `block`
+ .src_base_inst = struct_obj.zir_index,
+ .type_name_ctx = struct_obj.name,
+ };
+ defer assert(block.instructions.items.len == 0);
+
+ // We'll need to map the struct decl instruction to provide result types
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index});
+
+ const zir_struct = sema.code.getStructDecl(zir_index);
+ var field_it = zir_struct.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const default_val_src: LazySrcLoc = .{
+ .base_node_inst = struct_obj.zir_index,
+ .offset = .{ .container_field_value = zir_field.idx },
+ };
+ block.comptime_reason = .{ .reason = .{
+ .src = default_val_src,
+ .r = .{ .simple = .struct_field_default_value },
+ } };
+ const default_body = zir_field.default_body orelse {
+ struct_obj.field_defaults.get(ip)[zir_field.idx] = .none;
+ continue;
+ };
+ const field_ty: Type = .fromInterned(field_types[zir_field.idx]);
+ const uncoerced = ref: {
+ // Provide the result type
+ sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(field_ty.toIntern()));
+ defer assert(sema.inst_map.remove(zir_index));
+ break :ref try sema.resolveInlineBody(&block, default_body, zir_index);
+ };
+ const coerced = try sema.coerce(&block, field_ty, uncoerced, default_val_src);
+ const default_val = try sema.resolveConstValue(&block, default_val_src, coerced, null);
+ if (default_val.canMutateComptimeVarState(zcu)) {
+ const field_name = struct_obj.field_names.get(ip)[zir_field.idx];
+ return sema.failWithContainsReferenceToComptimeVar(&block, default_val_src, field_name, "field default value", default_val);
+ }
+ struct_obj.field_defaults.get(ip)[zir_field.idx] = default_val.toIntern();
+ }
+}
+
+/// This logic must be kept in sync with `Type.getUnionLayout`.
+pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ assert(sema.owner.unwrap().type_layout == union_ty.toIntern());
+
+ const union_obj = ip.loadUnionType(union_ty.toIntern());
+ const zir_index = union_obj.zir_index.resolve(ip).?;
+
+ assert(union_obj.layout != .@"packed");
+
+ try sema.declareDependency(.{ .src_hash = union_obj.zir_index });
+
+ var block: Block = .{
+ .parent = null,
+ .sema = sema,
+ .namespace = union_obj.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .comptime_reason = undefined, // always set before using `block`
+ .src_base_inst = union_obj.zir_index,
+ .type_name_ctx = union_obj.name,
+ };
+ defer assert(block.instructions.items.len == 0);
+
+ const zir_union = sema.code.getUnionDecl(zir_index);
+ var field_it = zir_union.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const field_ty_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = .{ .container_field_type = zir_field.idx },
+ };
+ const field_align_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = .{ .container_field_align = zir_field.idx },
+ };
+
+ const field_ty: Type = field_ty: {
+ block.comptime_reason = .{ .reason = .{
+ .src = field_ty_src,
+ .r = .{ .simple = .union_field_types },
+ } };
+ const type_body = zir_field.type_body orelse break :field_ty .void;
+ const type_ref = try sema.resolveInlineBody(&block, type_body, zir_index);
+ break :field_ty try sema.analyzeAsType(&block, field_ty_src, type_ref);
+ };
+ assert(!field_ty.isGenericPoison());
+ union_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
+
+ try sema.ensureLayoutResolved(field_ty);
+
+ const explicit_field_align: Alignment = a: {
+ block.comptime_reason = .{ .reason = .{
+ .src = field_align_src,
+ .r = .{ .simple = .union_field_attrs },
+ } };
+ const align_body = zir_field.align_body orelse break :a .none;
+ const align_ref = try sema.resolveInlineBody(&block, align_body, zir_index);
+ break :a try sema.analyzeAsAlign(&block, field_align_src, align_ref);
+ };
+
+ if (union_obj.field_aligns.len != 0) {
+ union_obj.field_aligns.get(ip)[zir_field.idx] = explicit_field_align;
+ } else {
+ assert(explicit_field_align == .none);
+ }
+
+ if (field_ty.zigTypeTag(zcu) == .@"opaque") {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ if (union_obj.layout == .@"extern" and !try sema.validateExternType(field_ty, .union_field)) {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.explainWhyTypeIsNotExtern(msg, field_ty_src, field_ty, .union_field);
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ }
+
+ try finishUnionLayout(
+ sema,
+ &block,
+ union_ty.srcLoc(zcu),
+ union_ty.toIntern(),
+ &union_obj,
+ .fromInterned(union_obj.enum_tag_type),
+ );
+}
+
+/// Called after populating field types and alignments; populates overall union layout
+/// information (size, alignment, comptime-only state, etc).
+pub fn finishUnionLayout(
+ sema: *Sema,
+ /// Only used to report compile errors.
+ block: *Block,
+ union_src: LazySrcLoc,
+ union_ty: InternPool.Index,
+ union_obj: *const InternPool.LoadedUnionType,
+ enum_tag_ty: Type,
+) SemaError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+
+ var payload_align: Alignment = .@"1";
+ var payload_size: u64 = 0;
+ var comptime_only = false;
+ var possible_values: enum { none, one, many } = .none;
+ for (0..union_obj.field_types.len) |field_idx| {
+ const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
+ const field_align: Alignment = a: {
+ if (union_obj.field_aligns.len != 0) {
+ const a = union_obj.field_aligns.get(ip)[field_idx];
+ if (a != .none) break :a a;
+ }
+ break :a field_ty.abiAlignment(zcu);
+ };
+ payload_align = payload_align.maxStrict(field_align);
+ payload_size = @max(payload_size, field_ty.abiSize(zcu));
+ if (field_ty.comptimeOnly(zcu)) comptime_only = true;
+ if (!field_ty.isNoReturn(zcu)) {
+ if (try field_ty.onePossibleValue(pt) != null) {
+ possible_values = .many; // this field alone has many possible values
+ } else switch (possible_values) {
+ .none => possible_values = .one, // there were none, now there is this field's OPV
+ .one => possible_values = .many, // there was one, now there are two
+ .many => {},
+ }
+ }
+ }
+
+ const size: u64, const padding: u64, const alignment: Alignment = layout: {
+ if (union_obj.runtime_tag == .none) {
+ break :layout .{ payload_align.forward(payload_size), 0, payload_align };
+ }
+ const tag_align = enum_tag_ty.abiAlignment(zcu);
+ const tag_size = enum_tag_ty.abiSize(zcu);
+ // The layout will either be (tag, payload, padding) or (payload, tag, padding) depending on
+ // which has larger alignment. So the overall size is just the tag and payload sizes, added,
+ // and padded to the larger alignment.
+ const alignment = tag_align.maxStrict(payload_align);
+ const unpadded_size = tag_size + payload_size;
+ const size = alignment.forward(unpadded_size);
+ break :layout .{ size, size - unpadded_size, alignment };
+ };
+
+ const casted_size = std.math.cast(u32, size) orelse return sema.fail(
+ block,
+ union_src,
+ "union layout requires size {d}, this compiler implementation supports up to {d}",
+ .{ size, std.math.maxInt(u32) },
+ );
+ ip.resolveUnionLayout(
+ io,
+ union_ty,
+ casted_size,
+ @intCast(padding), // okay because padding is no greater than size
+ alignment,
+ possible_values == .none, // MLUGG TODO: make sure queries use `LoadedUnionType.has_no_possible_value`!
+ possible_values == .one,
+ comptime_only,
+ );
+}
+
+pub fn resolvePackedUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ assert(sema.owner.unwrap().type_layout == union_ty.toIntern());
+
+ const union_obj = ip.loadUnionType(union_ty.toIntern());
+ const zir_index = union_obj.zir_index.resolve(ip).?;
+
+ assert(union_obj.layout == .@"packed");
+
+ try sema.declareDependency(.{ .src_hash = union_obj.zir_index });
+
+ var block: Block = .{
+ .parent = null,
+ .sema = sema,
+ .namespace = union_obj.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .comptime_reason = undefined, // always set before using `block`
+ .src_base_inst = union_obj.zir_index,
+ .type_name_ctx = union_obj.name,
+ };
+ defer assert(block.instructions.items.len == 0);
+
+ const zir_union = sema.code.getUnionDecl(zir_index);
+ var field_it = zir_union.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const field_ty_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = .{ .container_field_type = zir_field.idx },
+ };
+ const field_ty: Type = field_ty: {
+ block.comptime_reason = .{ .reason = .{
+ .src = field_ty_src,
+ .r = .{ .simple = .union_field_types },
+ } };
+ // MLUGG TODO: i think this should probably be a compile error? (if so, it's an astgen one, right?)
+ const type_body = zir_field.type_body orelse break :field_ty .void;
+ const type_ref = try sema.resolveInlineBody(&block, type_body, zir_index);
+ break :field_ty try sema.analyzeAsType(&block, field_ty_src, type_ref);
+ };
+ assert(!field_ty.isGenericPoison());
+ union_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
+
+ assert(zir_field.align_body == null); // packed union fields cannot be aligned
+ assert(zir_field.value_body == null); // packed union fields cannot have tag values
+
+ try sema.ensureLayoutResolved(field_ty);
+
+ if (field_ty.zigTypeTag(zcu) == .@"opaque") {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ if (!field_ty.packable(zcu)) {
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_ty_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ try sema.explainWhyTypeIsNotPackable(msg, field_ty_src, field_ty);
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ assert(!field_ty.comptimeOnly(zcu)); // packable types are not comptime-only
+ }
+
+ try resolvePackedUnionBackingInt(sema, &block, union_ty, &union_obj, false);
+}
+
+/// MLUGG TODO doc comment; asserts all fields are resolved or whatever
+pub fn resolvePackedUnionBackingInt(
+ sema: *Sema,
+ block: *Block,
+ union_ty: Type,
+ union_obj: *const InternPool.LoadedUnionType,
+ is_reified: bool,
+) SemaError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const io = comp.io;
+ const ip = &zcu.intern_pool;
+ switch (union_obj.packed_backing_mode) {
+ .explicit => {
+ const backing_int_type: Type = .fromInterned(union_obj.packed_backing_int_type);
+ const backing_int_bits = backing_int_type.intInfo(zcu).bits;
+ for (union_obj.field_types.get(ip), 0..) |field_type_ip, field_idx| {
+ const field_type: Type = .fromInterned(field_type_ip);
+ const field_bits = field_type.bitSize(zcu);
+ if (field_bits != backing_int_bits) return sema.failWithOwnedErrorMsg(block, msg: {
+ const field_ty_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = if (is_reified)
+ .nodeOffset(.zero)
+ else
+ .{ .container_field_type = @intCast(field_idx) },
+ };
+ const msg = try sema.errMsg(field_ty_src, "field bit width does not match backing integer", .{});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits });
+ try sema.errNote(field_ty_src, msg, "backing integer '{f}' has bit width '{d}'", .{ backing_int_type.fmt(pt), backing_int_bits });
+ try sema.errNote(field_ty_src, msg, "all fields in a packed union must have the same bit width", .{});
+ break :msg msg;
+ });
+ }
+ },
+ .auto => switch (union_obj.field_types.len) {
+ 0 => ip.resolvePackedUnionBackingInt(io, union_ty.toIntern(), .u0_type),
+ else => {
+ const field_types = union_obj.field_types.get(ip);
+ const first_field_type: Type = .fromInterned(field_types[0]);
+ const first_field_bits = first_field_type.bitSize(zcu);
+ for (field_types[1..], 1..) |field_type_ip, field_idx| {
+ const field_type: Type = .fromInterned(field_type_ip);
+ const field_bits = field_type.bitSize(zcu);
+ if (field_bits != first_field_bits) return sema.failWithOwnedErrorMsg(block, msg: {
+ const first_field_ty_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = if (is_reified)
+ .nodeOffset(.zero)
+ else
+ .{ .container_field_type = 0 },
+ };
+ const field_ty_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = if (is_reified)
+ .nodeOffset(.zero)
+ else
+ .{ .container_field_type = @intCast(field_idx) },
+ };
+ const msg = try sema.errMsg(field_ty_src, "field bit width does not match earlier field", .{});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits });
+ try sema.errNote(first_field_ty_src, msg, "other field type '{f}' has bit width '{d}'", .{ first_field_type.fmt(pt), first_field_bits });
+ try sema.errNote(field_ty_src, msg, "all fields in a packed union must have the same bit width", .{});
+ break :msg msg;
+ });
+ }
+ const backing_int_bits = std.math.cast(u16, first_field_bits) orelse return sema.fail(
+ block,
+ block.nodeOffset(.zero),
+ "packed union bit width '{d}' exceeds maximum bit width of 65535",
+ .{first_field_bits},
+ );
+ const backing_int_type = try pt.intType(.unsigned, backing_int_bits);
+ ip.resolvePackedUnionBackingInt(io, union_ty.toIntern(), backing_int_type.toIntern());
+ },
+ },
+ }
+}
+
+/// Asserts that `enum_ty` is an enum and that `sema.owner` is that type.
+/// This function *does* register the `src_hash` dependency on the enum.
+pub fn resolveEnumValues(sema: *Sema, enum_ty: Type) CompileError!void {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ assert(sema.owner.unwrap().type_inits == enum_ty.toIntern());
+
+ const enum_obj = ip.loadEnumType(enum_ty.toIntern());
+
+ // We'll populate this map.
+ const field_value_map = enum_obj.field_value_map.unwrap() orelse {
+ // The enum has an automatically generated tag and is auto-numbered. We know that we have
+ // generated a suitably large type in `analyzeEnumDecl`, so we have no work to do.
+ return;
+ };
+
+ const maybe_parent_union_obj: ?InternPool.LoadedUnionType = un: {
+ if (enum_obj.owner_union == .none) break :un null;
+ break :un ip.loadUnionType(enum_obj.owner_union);
+ };
+ const tracked_inst = enum_obj.zir_index.unwrap() orelse maybe_parent_union_obj.?.zir_index;
+ const zir_index = tracked_inst.resolve(ip).?;
+
+ try sema.declareDependency(.{ .src_hash = tracked_inst });
+
+ var block: Block = .{
+ .parent = null,
+ .sema = sema,
+ .namespace = enum_obj.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .comptime_reason = undefined, // always set before using `block`
+ .src_base_inst = tracked_inst,
+ .type_name_ctx = enum_obj.name,
+ };
+ defer assert(block.instructions.items.len == 0);
+
+ const int_tag_ty: Type = .fromInterned(enum_obj.int_tag_type);
+
+ // Map the enum (or union) decl instruction to provide the tag type as the result type
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index});
+ sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(int_tag_ty.toIntern()));
+ defer assert(sema.inst_map.remove(zir_index));
+
+ // First, populate any explicitly provided values. This is the part that actually depends on
+ // the ZIR, and hence depends on whether this is a declared or generated enum. If any explicit
+ // value is invalid, we'll emit an error here.
+ if (maybe_parent_union_obj) |union_obj| {
+ const zir_union = sema.code.getUnionDecl(zir_index);
+ var field_it = zir_union.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const field_val_src: LazySrcLoc = .{
+ .base_node_inst = union_obj.zir_index,
+ .offset = .{ .container_field_value = zir_field.idx },
+ };
+ block.comptime_reason = .{ .reason = .{
+ .src = field_val_src,
+ .r = .{ .simple = .enum_field_values },
+ } };
+ const value_body = zir_field.value_body orelse {
+ enum_obj.field_values.get(ip)[zir_field.idx] = .none;
+ continue;
+ };
+ const uncoerced = try sema.resolveInlineBody(&block, value_body, zir_index);
+ const coerced = try sema.coerce(&block, int_tag_ty, uncoerced, field_val_src);
+ const val = try sema.resolveConstValue(&block, field_val_src, coerced, null);
+ enum_obj.field_values.get(ip)[zir_field.idx] = val.toIntern();
+ }
+ } else {
+ const zir_enum = sema.code.getEnumDecl(zir_index);
+ var field_it = zir_enum.iterateFields();
+ while (field_it.next()) |zir_field| {
+ const field_val_src: LazySrcLoc = .{
+ .base_node_inst = enum_obj.zir_index.unwrap().?,
+ .offset = .{ .container_field_value = zir_field.idx },
+ };
+ block.comptime_reason = .{ .reason = .{
+ .src = field_val_src,
+ .r = .{ .simple = .enum_field_values },
+ } };
+ const value_body = zir_field.value_body orelse {
+ enum_obj.field_values.get(ip)[zir_field.idx] = .none;
+ continue;
+ };
+ const uncoerced = try sema.resolveInlineBody(&block, value_body, zir_index);
+ const coerced = try sema.coerce(&block, int_tag_ty, uncoerced, field_val_src);
+ const val = try sema.resolveConstDefinedValue(&block, field_val_src, coerced, null);
+ enum_obj.field_values.get(ip)[zir_field.idx] = val.toIntern();
+ }
+ }
+
+ // Explicit values are set. Now we'll go through the whole array and figure out the final
+ // field values. This is also where we'll detect duplicates.
+
+ for (0..enum_obj.field_names.len) |field_idx| {
+ const field_val_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_value = @intCast(field_idx) },
+ };
+ // If the field value was not specified, compute the implicit value.
+ const field_val = val: {
+ const explicit_val = enum_obj.field_values.get(ip)[field_idx];
+ if (explicit_val != .none) break :val explicit_val;
+ if (field_idx == 0) {
+ // Implicit value is 0, which is valid for every integer type.
+ const val = (try pt.intValue(int_tag_ty, 0)).toIntern();
+ enum_obj.field_values.get(ip)[field_idx] = val;
+ break :val val;
+ }
+ // Implicit non-initial value: take the previous field value and add one.
+ const prev_field_val: Value = .fromInterned(enum_obj.field_values.get(ip)[field_idx - 1]);
+ const result = try arith.incrementDefinedInt(sema, int_tag_ty, prev_field_val);
+ if (result.overflow) return sema.fail(
+ &block,
+ field_val_src,
+ "enum tag value '{f}' too large for type '{f}'",
+ .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) },
+ );
+ const val = result.val.toIntern();
+ enum_obj.field_values.get(ip)[field_idx] = val;
+ break :val val;
+ };
+ const adapter: InternPool.Index.Adapter = .{ .indexes = enum_obj.field_values.get(ip)[0..field_idx] };
+ const gop = field_value_map.get(ip).getOrPutAssumeCapacityAdapted(field_val, adapter);
+ if (!gop.found_existing) continue;
+ const prev_field_val_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_value = @intCast(gop.index) },
+ };
+ return sema.failWithOwnedErrorMsg(&block, msg: {
+ const msg = try sema.errMsg(field_val_src, "enum tag value '{f}' already taken", .{
+ Value.fromInterned(field_val).fmtValueSema(pt, sema),
+ });
+ errdefer msg.destroy(gpa);
+ try sema.errNote(prev_field_val_src, msg, "previous occurrence here", .{});
+ break :msg msg;
+ });
+ }
+
+ if (enum_obj.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
+ const fields_len = enum_obj.field_names.len;
+ if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {
+ return sema.fail(&block, block.nodeOffset(.zero), "non-exhaustive enum specifies every value", .{});
+ }
+ }
+}
diff --git a/src/Type.zig b/src/Type.zig
index 57d8a0a5ede75204e3382f4740f28c9b83799681..52dc5ed1ebb7918d930f51d21c65ba68a8178be5 100644
--- a/src/Type.zig
+++ b/src/Type.zig
@@ -12,12 +12,10 @@ const Target = std.Target;
const Zcu = @import("Zcu.zig");
const log = std.log.scoped(.Type);
const target_util = @import("target.zig");
-const Sema = @import("Sema.zig");
const InternPool = @import("InternPool.zig");
const Alignment = InternPool.Alignment;
const Zir = std.zig.Zir;
const Type = @This();
-const SemaError = Zcu.SemaError;
ip_index: InternPool.Index,
@@ -25,16 +23,6 @@ pub fn zigTypeTag(ty: Type, zcu: *const Zcu) std.builtin.TypeId {
return zcu.intern_pool.zigTypeTag(ty.toIntern());
}
-pub fn baseZigTypeTag(self: Type, mod: *Zcu) std.builtin.TypeId {
- return switch (self.zigTypeTag(mod)) {
- .error_union => self.errorUnionPayload(mod).baseZigTypeTag(mod),
- .optional => {
- return self.optionalChild(mod).baseZigTypeTag(mod);
- },
- else => |t| t,
- };
-}
-
/// Asserts the type is resolved.
pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
return switch (ty.zigTypeTag(zcu)) {
@@ -44,7 +32,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
.comptime_int,
=> true,
- .vector => ty.elemType2(zcu).isSelfComparable(zcu, is_equality_cmp),
+ .vector => ty.childType(zcu).isSelfComparable(zcu, is_equality_cmp),
.bool,
.type,
@@ -121,11 +109,7 @@ pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool {
return a.toIntern() == b.toIntern();
}
-pub fn format(ty: Type, writer: *std.Io.Writer) !void {
- _ = ty;
- _ = writer;
- @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
-}
+pub const format = @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
pub const Formatter = std.fmt.Alt(Format, Format.default);
@@ -440,31 +424,7 @@ pub fn toIntern(ty: Type) InternPool.Index {
}
pub fn toValue(self: Type) Value {
- return Value.fromInterned(self.toIntern());
-}
-
-const RuntimeBitsError = SemaError || error{NeedLazy};
-
-pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
- return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable;
-}
-
-pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
- return hasRuntimeBitsInner(ty, false, .sema, pt.zcu, pt.tid) catch |err| switch (err) {
- error.NeedLazy => unreachable, // this would require a resolve strat of lazy
- else => |e| return e,
- };
-}
-
-pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool {
- return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable;
-}
-
-pub fn hasRuntimeBitsIgnoreComptimeSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
- return hasRuntimeBitsInner(ty, true, .sema, pt.zcu, pt.tid) catch |err| switch (err) {
- error.NeedLazy => unreachable, // this would require a resolve strat of lazy
- else => |e| return e,
- };
+ return .fromInterned(self.toIntern());
}
/// true if and only if the type takes up space in memory at runtime.
@@ -476,205 +436,126 @@ pub fn hasRuntimeBitsIgnoreComptimeSema(ty: Type, pt: Zcu.PerThread) SemaError!b
/// * the type has only one possible value, making its ABI size 0.
/// - an enum with an explicit tag type has the ABI size of the integer tag type,
/// making it one-possible-value only if the integer tag type has 0 bits.
-/// When `ignore_comptime_only` is true, then types that are comptime-only
-/// may return false positives.
-pub fn hasRuntimeBitsInner(
- ty: Type,
- ignore_comptime_only: bool,
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) RuntimeBitsError!bool {
+pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
const ip = &zcu.intern_pool;
- const io = zcu.comp.io;
- return switch (ty.toIntern()) {
- .empty_tuple_type => false,
- else => switch (ip.indexToKey(ty.toIntern())) {
- .int_type => |int_type| int_type.bits != 0,
- .ptr_type => {
- // Pointers to zero-bit types still have a runtime address; however, pointers
- // to comptime-only types do not, with the exception of function pointers.
- if (ignore_comptime_only) return true;
- return switch (strat) {
- .sema => {
- const pt = strat.pt(zcu, tid);
- return !try ty.comptimeOnlySema(pt);
- },
- .eager => !ty.comptimeOnly(zcu),
- .lazy => error.NeedLazy,
- };
- },
- .anyframe_type => true,
- .array_type => |array_type| return array_type.lenIncludingSentinel() > 0 and
- try Type.fromInterned(array_type.child).hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid),
- .vector_type => |vector_type| return vector_type.len > 0 and
- try Type.fromInterned(vector_type.child).hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid),
- .opt_type => |child| {
- const child_ty = Type.fromInterned(child);
- if (child_ty.isNoReturn(zcu)) {
- // Then the optional is comptime-known to be null.
- return false;
- }
- if (ignore_comptime_only) return true;
- return switch (strat) {
- .sema => !try child_ty.comptimeOnlyInner(.sema, zcu, tid),
- .eager => !child_ty.comptimeOnly(zcu),
- .lazy => error.NeedLazy,
- };
- },
- .error_union_type,
- .error_set_type,
- .inferred_error_set_type,
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .int_type => |int_type| int_type.bits != 0,
+ .ptr_type => true,
+ .anyframe_type => true,
+ .array_type => |array_type| array_type.lenIncludingSentinel() > 0 and
+ Type.fromInterned(array_type.child).hasRuntimeBits(zcu),
+ .vector_type => |vector_type| vector_type.len > 0 and
+ Type.fromInterned(vector_type.child).hasRuntimeBits(zcu),
+ .opt_type => |child| !Type.fromInterned(child).isNoReturn(zcu),
+
+ .error_union_type,
+ .error_set_type,
+ .inferred_error_set_type,
+ => true,
+
+ // These are function *bodies*, not pointers.
+ // They return false here because they are comptime-only types.
+ // Special exceptions have to be made when emitting functions due to
+ // this returning false.
+ .func_type => false,
+
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .bool,
+ .anyerror,
+ .adhoc_inferred_error_set,
+ .anyopaque,
=> true,
- // These are function *bodies*, not pointers.
- // They return false here because they are comptime-only types.
- // Special exceptions have to be made when emitting functions due to
- // this returning false.
- .func_type => false,
-
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .bool,
- .anyerror,
- .adhoc_inferred_error_set,
- .anyopaque,
- => true,
-
- // These are false because they are comptime-only types.
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .null,
- .undefined,
- .enum_literal,
- => false,
-
- .generic_poison => unreachable,
- },
- .struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip, io)) {
- // In this case, we guess that hasRuntimeBits() for this type is true,
- // and then later if our guess was incorrect, we emit a compile error.
- return true;
- }
- switch (strat) {
- .sema => try ty.resolveFields(strat.pt(zcu, tid)),
- .eager => assert(struct_type.haveFieldTypes(ip)),
- .lazy => if (!struct_type.haveFieldTypes(ip)) return error.NeedLazy,
- }
- for (0..struct_type.field_types.len) |i| {
- if (struct_type.comptime_bits.getBit(ip, i)) continue;
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
- if (try field_ty.hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid))
- return true;
- } else {
- return false;
- }
- },
- .tuple_type => |tuple| {
- for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
- if (val != .none) continue; // comptime field
- if (try Type.fromInterned(field_ty).hasRuntimeBitsInner(
- ignore_comptime_only,
- strat,
- zcu,
- tid,
- )) return true;
- }
- return false;
- },
-
- .union_type => {
- const union_type = ip.loadUnionType(ty.toIntern());
- const union_flags = union_type.flagsUnordered(ip);
- switch (union_flags.runtime_tag) {
- .none => if (strat != .eager) {
- // In this case, we guess that hasRuntimeBits() for this type is true,
- // and then later if our guess was incorrect, we emit a compile error.
- if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip, io)) return true;
- },
- .safety, .tagged => {},
- }
- switch (strat) {
- .sema => try ty.resolveFields(strat.pt(zcu, tid)),
- .eager => assert(union_flags.status.haveFieldTypes()),
- .lazy => if (!union_flags.status.haveFieldTypes())
- return error.NeedLazy,
- }
- switch (union_flags.runtime_tag) {
- .none => {},
- .safety, .tagged => {
- const tag_ty = union_type.tagTypeUnordered(ip);
- assert(tag_ty != .none); // tag_ty should have been resolved above
- if (try Type.fromInterned(tag_ty).hasRuntimeBitsInner(
- ignore_comptime_only,
- strat,
- zcu,
- tid,
- )) {
- return true;
- }
- },
- }
- for (0..union_type.field_types.len) |field_index| {
- const field_ty = Type.fromInterned(union_type.field_types.get(ip)[field_index]);
- if (try field_ty.hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid))
- return true;
- } else {
- return false;
- }
- },
-
- .opaque_type => true,
- .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).hasRuntimeBitsInner(
- ignore_comptime_only,
- strat,
- zcu,
- tid,
- ),
-
- // values, not types
- .undef,
- .simple_value,
- .variable,
- .@"extern",
- .func,
- .int,
- .err,
- .error_union,
+ .void,
+ .noreturn,
+ => false,
+
+ // primitive comptime-only types
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
.enum_literal,
- .enum_tag,
- .empty_enum_value,
- .float,
- .ptr,
- .slice,
- .opt,
- .aggregate,
- .un,
- // memoization, not types
- .memoized_call,
- => unreachable,
+ => false,
+
+ .generic_poison => unreachable,
},
+ .struct_type => {
+ // TODO MLUGG: memoize this state when resolving struct?
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_idx| {
+ if (struct_obj.field_is_comptime_bits.get(ip, field_idx)) continue;
+ const field_ty: Type = .fromInterned(field_ty_ip);
+ if (field_ty.hasRuntimeBits(zcu)) return true;
+ }
+ return false;
+ },
+ .tuple_type => |tuple| {
+ for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
+ if (val != .none) continue; // comptime field
+ if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) return true;
+ }
+ return false;
+ },
+ .union_type => {
+ // TODO MLUGG: memoize this state when resolving union?
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ switch (union_obj.runtime_tag) {
+ .none => {},
+ .safety, .tagged => {
+ if (Type.fromInterned(union_obj.enum_tag_type).hasRuntimeBits(zcu)) return true;
+ },
+ }
+ for (union_obj.field_types.get(ip)) |field_ty_ip| {
+ const field_ty: Type = .fromInterned(field_ty_ip);
+ if (field_ty.hasRuntimeBits(zcu)) return true;
+ }
+ return false;
+ },
+
+ // MLUGG TODO: i think this can go away and the assert move to the defer?
+ .opaque_type => true,
+ .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).hasRuntimeBits(zcu),
+
+ // values, not types
+ .undef,
+ .simple_value,
+ .variable,
+ .@"extern",
+ .func,
+ .int,
+ .err,
+ .error_union,
+ .enum_literal,
+ .enum_tag,
+ .empty_enum_value,
+ .float,
+ .ptr,
+ .slice,
+ .opt,
+ .aggregate,
+ .un,
+ // memoization, not types
+ .memoized_call,
+ => unreachable,
};
}
@@ -739,16 +620,15 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
},
.struct_type => ip.loadStructType(ty.toIntern()).layout != .auto,
.union_type => {
- const union_type = ip.loadUnionType(ty.toIntern());
- return switch (union_type.flagsUnordered(ip).runtime_tag) {
- .none, .safety => union_type.flagsUnordered(ip).layout != .auto,
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ if (union_obj.layout == .auto) return false;
+ return switch (union_obj.runtime_tag) {
+ .none => true,
.tagged => false,
+ .safety => unreachable, // well-defined layout can't have a safety tag
};
},
- .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) {
- .auto => false,
- .explicit, .nonexhaustive => true,
- },
+ .enum_type => ip.loadEnumType(ty.toIntern()).int_tag_is_explicit,
// values, not types
.undef,
@@ -774,28 +654,20 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
};
}
-pub fn fnHasRuntimeBits(ty: Type, zcu: *Zcu) bool {
- return ty.fnHasRuntimeBitsInner(.normal, zcu, {}) catch unreachable;
-}
-
-pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
- return try ty.fnHasRuntimeBitsInner(.sema, pt.zcu, pt.tid);
-}
-
/// Determines whether a function type has runtime bits, i.e. whether a
/// function with this type can exist at runtime.
/// Asserts that `ty` is a function type.
-pub fn fnHasRuntimeBitsInner(
- ty: Type,
- comptime strat: ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!bool {
- const fn_info = zcu.typeToFunc(ty).?;
- if (fn_info.is_generic) return false;
- if (fn_info.is_var_args) return true;
+pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *Zcu) bool {
+ const fn_info = zcu.typeToFunc(fn_ty).?;
+ if (fn_info.comptime_bits != 0) return false;
+ for (fn_info.param_types.get(&zcu.intern_pool)) |param_ty| {
+ if (param_ty == .generic_poison_type) return false;
+ if (Type.fromInterned(param_ty).comptimeOnly(zcu)) return false;
+ }
+ if (fn_info.return_type == .generic_poison_type) return false;
+ if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) return false;
if (fn_info.cc == .@"inline") return false;
- return !try Type.fromInterned(fn_info.return_type).comptimeOnlyInner(strat, zcu, tid);
+ return true;
}
pub fn isFnOrHasRuntimeBits(ty: Type, zcu: *Zcu) bool {
@@ -806,10 +678,11 @@ pub fn isFnOrHasRuntimeBits(ty: Type, zcu: *Zcu) bool {
}
/// Same as `isFnOrHasRuntimeBits` but comptime-only types may return a false positive.
+/// MLUGG TODO: this function is a bit silly now...
pub fn isFnOrHasRuntimeBitsIgnoreComptime(ty: Type, zcu: *Zcu) bool {
return switch (ty.zigTypeTag(zcu)) {
.@"fn" => true,
- else => return ty.hasRuntimeBitsIgnoreComptime(zcu),
+ else => return ty.hasRuntimeBits(zcu),
};
}
@@ -818,29 +691,15 @@ pub fn isNoReturn(ty: Type, zcu: *const Zcu) bool {
}
/// Never returns `none`. Asserts that all necessary type resolution is already done.
-pub fn ptrAlignment(ty: Type, zcu: *Zcu) Alignment {
- return ptrAlignmentInner(ty, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {
- return try ty.ptrAlignmentInner(.sema, pt.zcu, pt.tid);
-}
-
-pub fn ptrAlignmentInner(
- ty: Type,
- comptime strat: ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) !Alignment {
- return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
- .ptr_type => |ptr_type| {
- if (ptr_type.flags.alignment != .none) return ptr_type.flags.alignment;
- const res = try Type.fromInterned(ptr_type.child).abiAlignmentInner(strat.toLazy(), zcu, tid);
- return res.scalar;
- },
- .opt_type => |child| Type.fromInterned(child).ptrAlignmentInner(strat, zcu, tid),
+pub fn ptrAlignment(ptr_ty: Type, zcu: *Zcu) Alignment {
+ const ip = &zcu.intern_pool;
+ const ptr_key: InternPool.Key.PtrType = switch (ip.indexToKey(ptr_ty.toIntern())) {
+ .ptr_type => |key| key,
+ .opt_type => |child| ip.indexToKey(child).ptr_type,
else => unreachable,
};
+ if (ptr_key.flags.alignment != .none) return ptr_key.flags.alignment;
+ return Type.fromInterned(ptr_key.child).abiAlignment(zcu);
}
pub fn ptrAddressSpace(ty: Type, zcu: *const Zcu) std.builtin.AddressSpace {
@@ -851,861 +710,347 @@ pub fn ptrAddressSpace(ty: Type, zcu: *const Zcu) std.builtin.AddressSpace {
};
}
-/// May capture a reference to `ty`.
-/// Returned value has type `comptime_int`.
-pub fn lazyAbiAlignment(ty: Type, pt: Zcu.PerThread) !Value {
- switch (try ty.abiAlignmentInner(.lazy, pt.zcu, pt.tid)) {
- .val => |val| return val,
- .scalar => |x| return pt.intValue(Type.comptime_int, x.toByteUnits() orelse 0),
- }
-}
-
-pub const AbiAlignmentInner = union(enum) {
- scalar: Alignment,
- val: Value,
-};
-
-pub const ResolveStratLazy = enum {
- /// Return a `lazy_size` or `lazy_align` value if necessary.
- /// This value can be resolved later using `Value.resolveLazy`.
- lazy,
- /// Return a scalar result, expecting all necessary type resolution to be completed.
- /// Backends should typically use this, since they must not perform type resolution.
- eager,
- /// Return a scalar result, performing type resolution as necessary.
- /// This should typically be used from semantic analysis.
- sema,
-
- pub fn Tid(strat: ResolveStratLazy) type {
- return switch (strat) {
- .lazy, .sema => Zcu.PerThread.Id,
- .eager => void,
- };
- }
-
- pub fn ZcuPtr(strat: ResolveStratLazy) type {
- return switch (strat) {
- .eager => *const Zcu,
- .sema, .lazy => *Zcu,
- };
- }
-
- pub fn pt(
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
- ) switch (strat) {
- .lazy, .sema => Zcu.PerThread,
- .eager => void,
- } {
- return switch (strat) {
- .lazy, .sema => .{ .tid = tid, .zcu = zcu },
- else => {},
- };
- }
-};
-
-/// The chosen strategy can be easily optimized away in release builds.
-/// However, in debug builds, it helps to avoid accidentally resolving types in backends.
-pub const ResolveStrat = enum {
- /// Assert that all necessary resolution is completed.
- /// Backends should typically use this, since they must not perform type resolution.
- normal,
- /// Perform type resolution as necessary using `Zcu`.
- /// This should typically be used from semantic analysis.
- sema,
-
- pub fn Tid(strat: ResolveStrat) type {
- return switch (strat) {
- .sema => Zcu.PerThread.Id,
- .normal => void,
- };
- }
-
- pub fn ZcuPtr(strat: ResolveStrat) type {
- return switch (strat) {
- .normal => *const Zcu,
- .sema => *Zcu,
- };
- }
-
- pub fn pt(comptime strat: ResolveStrat, zcu: strat.ZcuPtr(), tid: strat.Tid()) switch (strat) {
- .sema => Zcu.PerThread,
- .normal => void,
- } {
- return switch (strat) {
- .sema => .{ .tid = tid, .zcu = zcu },
- .normal => {},
- };
- }
-
- pub inline fn toLazy(strat: ResolveStrat) ResolveStratLazy {
- return switch (strat) {
- .normal => .eager,
- .sema => .sema,
- };
- }
-};
-
/// Never returns `none`. Asserts that all necessary type resolution is already done.
+/// MLUGG TODO: check that it really does never return `.none`
pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
- return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar;
-}
-
-pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {
- return (try ty.abiAlignmentInner(.sema, pt.zcu, pt.tid)).scalar;
-}
-
-/// If you pass `eager` you will get back `scalar` and assert the type is resolved.
-/// In this case there will be no error, guaranteed.
-/// If you pass `lazy` you may get back `scalar` or `val`.
-/// If `val` is returned, a reference to `ty` has been captured.
-/// If you pass `sema` you will get back `scalar` and resolve the type if
-/// necessary, possibly returning a CompileError.
-pub fn abiAlignmentInner(
- ty: Type,
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!AbiAlignmentInner {
- const pt = strat.pt(zcu, tid);
- const target = zcu.getTarget();
const ip = &zcu.intern_pool;
-
- switch (ty.toIntern()) {
- .empty_tuple_type => return .{ .scalar = .@"1" },
- else => switch (ip.indexToKey(ty.toIntern())) {
- .int_type => |int_type| {
- if (int_type.bits == 0) return .{ .scalar = .@"1" };
- return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, int_type.bits)) };
- },
- .ptr_type, .anyframe_type => {
- return .{ .scalar = ptrAbiAlignment(target) };
- },
- .array_type => |array_type| {
- return Type.fromInterned(array_type.child).abiAlignmentInner(strat, zcu, tid);
- },
- .vector_type => |vector_type| {
- if (vector_type.len == 0) return .{ .scalar = .@"1" };
- switch (zcu.comp.getZigBackend()) {
- else => {
- // This is fine because the child type of a vector always has a bit-size known
- // without needing any type resolution.
- const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu));
- if (elem_bits == 0) return .{ .scalar = .@"1" };
- const bytes = ((elem_bits * vector_type.len) + 7) / 8;
- const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes);
- return .{ .scalar = Alignment.fromByteUnits(alignment) };
- },
- .stage2_c => {
- return Type.fromInterned(vector_type.child).abiAlignmentInner(strat, zcu, tid);
- },
- .stage2_x86_64 => {
- if (vector_type.child == .bool_type) {
- if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .{ .scalar = .@"64" };
- if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .{ .scalar = .@"32" };
- if (vector_type.len > 64) return .{ .scalar = .@"16" };
- const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable;
- const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes);
- return .{ .scalar = Alignment.fromByteUnits(alignment) };
- }
- const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeInner(strat, zcu, tid)).scalar);
- if (elem_bytes == 0) return .{ .scalar = .@"1" };
- const bytes = elem_bytes * vector_type.len;
- if (bytes > 32 and target.cpu.has(.x86, .avx512f)) return .{ .scalar = .@"64" };
- if (bytes > 16 and target.cpu.has(.x86, .avx)) return .{ .scalar = .@"32" };
- return .{ .scalar = .@"16" };
- },
- }
- },
-
- .opt_type => return ty.abiAlignmentInnerOptional(strat, zcu, tid),
- .error_union_type => |info| return ty.abiAlignmentInnerErrorUnion(
- strat,
- zcu,
- tid,
- Type.fromInterned(info.payload_type),
- ),
-
- .error_set_type, .inferred_error_set_type => {
- const bits = zcu.errorSetBits();
- if (bits == 0) return .{ .scalar = .@"1" };
- return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, bits)) };
- },
-
- // represents machine code; not a pointer
- .func_type => return .{ .scalar = target_util.minFunctionAlignment(target) },
-
- .simple_type => |t| switch (t) {
- .bool,
- .anyopaque,
- => return .{ .scalar = .@"1" },
-
- .usize,
- .isize,
- => return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())) },
-
- .c_char => return .{ .scalar = cTypeAlign(target, .char) },
- .c_short => return .{ .scalar = cTypeAlign(target, .short) },
- .c_ushort => return .{ .scalar = cTypeAlign(target, .ushort) },
- .c_int => return .{ .scalar = cTypeAlign(target, .int) },
- .c_uint => return .{ .scalar = cTypeAlign(target, .uint) },
- .c_long => return .{ .scalar = cTypeAlign(target, .long) },
- .c_ulong => return .{ .scalar = cTypeAlign(target, .ulong) },
- .c_longlong => return .{ .scalar = cTypeAlign(target, .longlong) },
- .c_ulonglong => return .{ .scalar = cTypeAlign(target, .ulonglong) },
- .c_longdouble => return .{ .scalar = cTypeAlign(target, .longdouble) },
-
- .f16 => return .{ .scalar = .@"2" },
- .f32 => return .{ .scalar = cTypeAlign(target, .float) },
- .f64 => switch (target.cTypeBitSize(.double)) {
- 64 => return .{ .scalar = cTypeAlign(target, .double) },
- else => return .{ .scalar = .@"8" },
- },
- .f80 => switch (target.cTypeBitSize(.longdouble)) {
- 80 => return .{ .scalar = cTypeAlign(target, .longdouble) },
- else => return .{ .scalar = Type.u80.abiAlignment(zcu) },
+ const target = zcu.getTarget();
+ assertHasLayout(ty, zcu);
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .int_type => |int_type| {
+ if (int_type.bits == 0) return .@"1";
+ return .fromByteUnits(std.zig.target.intAlignment(target, int_type.bits));
+ },
+ .ptr_type, .anyframe_type => ptrAbiAlignment(target),
+ .array_type => |array_type| Type.fromInterned(array_type.child).abiAlignment(zcu),
+ .vector_type => |vector_type| {
+ if (vector_type.len == 0) return .@"1";
+ switch (zcu.comp.getZigBackend()) {
+ else => {
+ const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu));
+ if (elem_bits == 0) return .@"1";
+ const bytes = ((elem_bits * vector_type.len) + 7) / 8;
+ return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
},
- .f128 => switch (target.cTypeBitSize(.longdouble)) {
- 128 => return .{ .scalar = cTypeAlign(target, .longdouble) },
- else => return .{ .scalar = .@"16" },
- },
-
- .anyerror, .adhoc_inferred_error_set => {
- const bits = zcu.errorSetBits();
- if (bits == 0) return .{ .scalar = .@"1" };
- return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, bits)) };
- },
-
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- => return .{ .scalar = .@"1" },
-
- .noreturn => unreachable,
- .generic_poison => unreachable,
- },
- .struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- if (struct_type.layout == .@"packed") {
- switch (strat) {
- .sema => try ty.resolveLayout(pt),
- .lazy => if (struct_type.backingIntTypeUnordered(ip) == .none) return .{
- .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })),
- },
- .eager => {},
- }
- return .{ .scalar = Type.fromInterned(struct_type.backingIntTypeUnordered(ip)).abiAlignment(zcu) };
- }
-
- if (struct_type.flagsUnordered(ip).alignment == .none) switch (strat) {
- .eager => unreachable, // struct alignment not resolved
- .sema => try ty.resolveStructAlignment(pt),
- .lazy => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) },
- };
-
- return .{ .scalar = struct_type.flagsUnordered(ip).alignment };
- },
- .tuple_type => |tuple| {
- var big_align: Alignment = .@"1";
- for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
- if (val != .none) continue; // comptime field
- switch (try Type.fromInterned(field_ty).abiAlignmentInner(strat, zcu, tid)) {
- .scalar => |field_align| big_align = big_align.max(field_align),
- .val => switch (strat) {
- .eager => unreachable, // field type alignment not resolved
- .sema => unreachable, // passed to abiAlignmentInner above
- .lazy => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) },
- },
+ .stage2_c => return Type.fromInterned(vector_type.child).abiAlignment(zcu),
+ .stage2_x86_64 => {
+ if (vector_type.child == .bool_type) {
+ if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";
+ if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";
+ if (vector_type.len > 64) return .@"16";
+ const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable;
+ return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
}
- }
- return .{ .scalar = big_align };
- },
- .union_type => {
- const union_type = ip.loadUnionType(ty.toIntern());
-
- if (union_type.flagsUnordered(ip).alignment == .none) switch (strat) {
- .eager => unreachable, // union layout not resolved
- .sema => try ty.resolveUnionAlignment(pt),
- .lazy => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) },
- };
-
- return .{ .scalar = union_type.flagsUnordered(ip).alignment };
- },
- .opaque_type => return .{ .scalar = .@"1" },
- .enum_type => return .{
- .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiAlignment(zcu),
- },
-
- // values, not types
- .undef,
- .simple_value,
- .variable,
- .@"extern",
- .func,
- .int,
- .err,
- .error_union,
+ const elem_bytes: u32 = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu));
+ if (elem_bytes == 0) return .@"1";
+ const bytes = elem_bytes * vector_type.len;
+ if (bytes > 32 and target.cpu.has(.x86, .avx512f)) return .@"64";
+ if (bytes > 16 and target.cpu.has(.x86, .avx)) return .@"32";
+ return .@"16";
+ },
+ }
+ },
+
+ .opt_type => |child| Type.fromInterned(child).abiAlignment(zcu),
+ .error_union_type => |eu| Alignment.maxStrict(
+ Type.fromInterned(eu.payload_type).abiAlignment(zcu),
+ errorAbiAlignment(zcu),
+ ),
+
+ .error_set_type, .inferred_error_set_type => errorAbiAlignment(zcu),
+
+ .func_type => target_util.minFunctionAlignment(target),
+
+ .simple_type => |t| switch (t) {
+ .bool,
+ .void,
+ .noreturn,
+ .anyopaque,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
.enum_literal,
- .enum_tag,
- .empty_enum_value,
- .float,
- .ptr,
- .slice,
- .opt,
- .aggregate,
- .un,
- // memoization, not types
- .memoized_call,
- => unreachable,
- },
- }
-}
+ => .@"1",
-fn abiAlignmentInnerErrorUnion(
- ty: Type,
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
- payload_ty: Type,
-) SemaError!AbiAlignmentInner {
- // This code needs to be kept in sync with the equivalent switch prong
- // in abiSizeInner.
- const code_align = Type.anyerror.abiAlignment(zcu);
- switch (strat) {
- .eager, .sema => {
- if (!(payload_ty.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) {
- error.NeedLazy => if (strat == .lazy) {
- const pt = strat.pt(zcu, tid);
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) };
- } else unreachable,
- else => |e| return e,
- })) {
- return .{ .scalar = code_align };
- }
- return .{ .scalar = code_align.max(
- (try payload_ty.abiAlignmentInner(strat, zcu, tid)).scalar,
- ) };
- },
- .lazy => {
- const pt = strat.pt(zcu, tid);
- switch (try payload_ty.abiAlignmentInner(strat, zcu, tid)) {
- .scalar => |payload_align| return .{ .scalar = code_align.max(payload_align) },
- .val => {},
- }
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) };
- },
- }
-}
+ .anyerror, .adhoc_inferred_error_set => errorAbiAlignment(zcu),
+ .usize, .isize => .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
-fn abiAlignmentInnerOptional(
- ty: Type,
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!AbiAlignmentInner {
- const pt = strat.pt(zcu, tid);
- const target = zcu.getTarget();
- const child_type = ty.optionalChild(zcu);
+ .c_char => cTypeAlign(target, .char),
+ .c_short => cTypeAlign(target, .short),
+ .c_ushort => cTypeAlign(target, .ushort),
+ .c_int => cTypeAlign(target, .int),
+ .c_uint => cTypeAlign(target, .uint),
+ .c_long => cTypeAlign(target, .long),
+ .c_ulong => cTypeAlign(target, .ulong),
+ .c_longlong => cTypeAlign(target, .longlong),
+ .c_ulonglong => cTypeAlign(target, .ulonglong),
+ .c_longdouble => cTypeAlign(target, .longdouble),
- switch (child_type.zigTypeTag(zcu)) {
- .pointer => return .{ .scalar = ptrAbiAlignment(target) },
- .error_set => return Type.anyerror.abiAlignmentInner(strat, zcu, tid),
- .noreturn => return .{ .scalar = .@"1" },
- else => {},
- }
+ .f16 => .@"2",
+ .f32 => cTypeAlign(target, .float),
+ .f64 => switch (target.cTypeBitSize(.double)) {
+ 64 => cTypeAlign(target, .double),
+ else => .@"8",
+ },
+ .f80 => switch (target.cTypeBitSize(.longdouble)) {
+ 80 => cTypeAlign(target, .longdouble),
+ else => Type.u80.abiAlignment(zcu),
+ },
+ .f128 => switch (target.cTypeBitSize(.longdouble)) {
+ 128 => cTypeAlign(target, .longdouble),
+ else => .@"16",
+ },
- switch (strat) {
- .eager, .sema => {
- if (!(child_type.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) {
- error.NeedLazy => if (strat == .lazy) {
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) };
- } else unreachable,
- else => |e| return e,
- })) {
- return .{ .scalar = .@"1" };
+ .generic_poison => unreachable,
+ },
+ .tuple_type => |tuple| {
+ var big_align: Alignment = .@"1";
+ for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
+ if (val != .none) continue; // comptime field
+ const field_align = Type.fromInterned(field_ty).abiAlignment(zcu);
+ big_align = big_align.max(field_align);
}
- return child_type.abiAlignmentInner(strat, zcu, tid);
+ return big_align;
},
- .lazy => switch (try child_type.abiAlignmentInner(strat, zcu, tid)) {
- .scalar => |x| return .{ .scalar = x.max(.@"1") },
- .val => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_align = ty.toIntern() },
- } })) },
+ .struct_type => {
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ switch (struct_obj.layout) {
+ .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiAlignment(zcu),
+ .auto, .@"extern" => return struct_obj.alignment,
+ }
},
- }
-}
+ .union_type => {
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ switch (union_obj.layout) {
+ .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiAlignment(zcu),
+ .auto, .@"extern" => return getUnionLayout(union_obj, zcu).abi_align,
+ }
+ },
+ .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiAlignment(zcu),
+ .opaque_type => .@"1",
-const AbiSizeInner = union(enum) {
- scalar: u64,
- val: Value,
-};
+ // values, not types
+ .undef,
+ .simple_value,
+ .variable,
+ .@"extern",
+ .func,
+ .int,
+ .err,
+ .error_union,
+ .enum_literal,
+ .enum_tag,
+ .empty_enum_value,
+ .float,
+ .ptr,
+ .slice,
+ .opt,
+ .aggregate,
+ .un,
+ // memoization, not types
+ .memoized_call,
+ => unreachable,
+ };
+}
-/// Asserts the type has the ABI size already resolved.
-/// Types that return false for hasRuntimeBits() return 0.
+/// Asserts that `ty` is not an opaque type.
pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
- return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar;
-}
-
-/// May capture a reference to `ty`.
-pub fn abiSizeLazy(ty: Type, pt: Zcu.PerThread) !Value {
- switch (try ty.abiSizeInner(.lazy, pt.zcu, pt.tid)) {
- .val => |val| return val,
- .scalar => |x| return pt.intValue(Type.comptime_int, x),
- }
-}
-
-pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {
- return (try abiSizeInner(ty, .sema, pt.zcu, pt.tid)).scalar;
-}
-
-/// If you pass `eager` you will get back `scalar` and assert the type is resolved.
-/// In this case there will be no error, guaranteed.
-/// If you pass `lazy` you may get back `scalar` or `val`.
-/// If `val` is returned, a reference to `ty` has been captured.
-/// If you pass `sema` you will get back `scalar` and resolve the type if
-/// necessary, possibly returning a CompileError.
-pub fn abiSizeInner(
- ty: Type,
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!AbiSizeInner {
- const target = zcu.getTarget();
const ip = &zcu.intern_pool;
-
- switch (ty.toIntern()) {
- .empty_tuple_type => return .{ .scalar = 0 },
-
- else => switch (ip.indexToKey(ty.toIntern())) {
- .int_type => |int_type| {
- if (int_type.bits == 0) return .{ .scalar = 0 };
- return .{ .scalar = std.zig.target.intByteSize(target, int_type.bits) };
- },
- .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
- .slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 },
- else => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) },
- },
- .anyframe_type => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) },
-
- .array_type => |array_type| {
- const len = array_type.lenIncludingSentinel();
- if (len == 0) return .{ .scalar = 0 };
- switch (try Type.fromInterned(array_type.child).abiSizeInner(strat, zcu, tid)) {
- .scalar => |elem_size| return .{ .scalar = len * elem_size },
- .val => switch (strat) {
- .sema, .eager => unreachable,
- .lazy => {
- const pt = strat.pt(zcu, tid);
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })) };
- },
- },
- }
- },
- .vector_type => |vector_type| {
- const sub_strat: ResolveStrat = switch (strat) {
- .sema => .sema,
- .eager => .normal,
- .lazy => {
- const pt = strat.pt(zcu, tid);
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })) };
- },
- };
- const alignment = (try ty.abiAlignmentInner(strat, zcu, tid)).scalar;
- const total_bytes = switch (zcu.comp.getZigBackend()) {
- else => total_bytes: {
- const elem_bits = try Type.fromInterned(vector_type.child).bitSizeInner(sub_strat, zcu, tid);
- const total_bits = elem_bits * vector_type.len;
- break :total_bytes (total_bits + 7) / 8;
- },
- .stage2_c => total_bytes: {
- const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeInner(strat, zcu, tid)).scalar);
- break :total_bytes elem_bytes * vector_type.len;
- },
- .stage2_x86_64 => total_bytes: {
- if (vector_type.child == .bool_type) break :total_bytes std.math.divCeil(u32, vector_type.len, 8) catch unreachable;
- const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeInner(strat, zcu, tid)).scalar);
- break :total_bytes elem_bytes * vector_type.len;
- },
- };
- return .{ .scalar = alignment.forward(total_bytes) };
- },
-
- .opt_type => return ty.abiSizeInnerOptional(strat, zcu, tid),
-
- .error_set_type, .inferred_error_set_type => {
- const bits = zcu.errorSetBits();
- if (bits == 0) return .{ .scalar = 0 };
- return .{ .scalar = std.zig.target.intByteSize(target, bits) };
- },
-
- .error_union_type => |error_union_type| {
- const payload_ty = Type.fromInterned(error_union_type.payload_type);
- // This code needs to be kept in sync with the equivalent switch prong
- // in abiAlignmentInner.
- const code_size = Type.anyerror.abiSize(zcu);
- if (!(payload_ty.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) {
- error.NeedLazy => if (strat == .lazy) {
- const pt = strat.pt(zcu, tid);
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })) };
- } else unreachable,
- else => |e| return e,
- })) {
- // Same as anyerror.
- return .{ .scalar = code_size };
- }
- const code_align = Type.anyerror.abiAlignment(zcu);
- const payload_align = (try payload_ty.abiAlignmentInner(strat, zcu, tid)).scalar;
- const payload_size = switch (try payload_ty.abiSizeInner(strat, zcu, tid)) {
- .scalar => |elem_size| elem_size,
- .val => switch (strat) {
- .sema => unreachable,
- .eager => unreachable,
- .lazy => {
- const pt = strat.pt(zcu, tid);
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })) };
- },
- },
- };
-
- var size: u64 = 0;
- if (code_align.compare(.gt, payload_align)) {
- size += code_size;
- size = payload_align.forward(size);
- size += payload_size;
- size = code_align.forward(size);
- } else {
- size += payload_size;
- size = code_align.forward(size);
- size += code_size;
- size = payload_align.forward(size);
- }
- return .{ .scalar = size };
- },
- .func_type => unreachable, // represents machine code; not a pointer
- .simple_type => |t| switch (t) {
- .bool => return .{ .scalar = 1 },
-
- .f16 => return .{ .scalar = 2 },
- .f32 => return .{ .scalar = 4 },
- .f64 => return .{ .scalar = 8 },
- .f128 => return .{ .scalar = 16 },
- .f80 => switch (target.cTypeBitSize(.longdouble)) {
- 80 => return .{ .scalar = target.cTypeByteSize(.longdouble) },
- else => return .{ .scalar = Type.u80.abiSize(zcu) },
+ const target = zcu.getTarget();
+ assertHasLayout(ty, zcu);
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .int_type => |int_type| std.zig.target.intByteSize(target, int_type.bits),
+ .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
+ .slice => ptrAbiSize(target) * 2,
+ .one, .many, .c => ptrAbiSize(target),
+ },
+ .anyframe_type => ptrAbiSize(target),
+ .array_type => |arr| arr.lenIncludingSentinel() * Type.fromInterned(arr.child).abiSize(zcu),
+ .vector_type => |vec| {
+ const elem_ty: Type = .fromInterned(vec.child);
+ const bytes = switch (zcu.comp.getZigBackend()) {
+ else => std.math.divCeil(u64, vec.len * elem_ty.bitSize(zcu), 8) catch unreachable,
+ .stage2_c => vec.len * elem_ty.abiSize(zcu),
+ .stage2_x86_64 => switch (elem_ty.toIntern()) {
+ .bool_type => std.math.divCeil(u64, vec.len, 8) catch unreachable,
+ else => vec.len * elem_ty.abiSize(zcu),
},
-
- .usize,
- .isize,
- => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) },
-
- .c_char => return .{ .scalar = target.cTypeByteSize(.char) },
- .c_short => return .{ .scalar = target.cTypeByteSize(.short) },
- .c_ushort => return .{ .scalar = target.cTypeByteSize(.ushort) },
- .c_int => return .{ .scalar = target.cTypeByteSize(.int) },
- .c_uint => return .{ .scalar = target.cTypeByteSize(.uint) },
- .c_long => return .{ .scalar = target.cTypeByteSize(.long) },
- .c_ulong => return .{ .scalar = target.cTypeByteSize(.ulong) },
- .c_longlong => return .{ .scalar = target.cTypeByteSize(.longlong) },
- .c_ulonglong => return .{ .scalar = target.cTypeByteSize(.ulonglong) },
- .c_longdouble => return .{ .scalar = target.cTypeByteSize(.longdouble) },
-
- .anyopaque,
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- => return .{ .scalar = 0 },
-
- .anyerror, .adhoc_inferred_error_set => {
- const bits = zcu.errorSetBits();
- if (bits == 0) return .{ .scalar = 0 };
- return .{ .scalar = std.zig.target.intByteSize(target, bits) };
- },
-
- .noreturn => unreachable,
- .generic_poison => unreachable,
- },
- .struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- switch (strat) {
- .sema => try ty.resolveLayout(strat.pt(zcu, tid)),
- .lazy => {
- const pt = strat.pt(zcu, tid);
- switch (struct_type.layout) {
- .@"packed" => {
- if (struct_type.backingIntTypeUnordered(ip) == .none) return .{
- .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })),
- };
- },
- .auto, .@"extern" => {
- if (!struct_type.haveLayout(ip)) return .{
- .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })),
- };
- },
- }
- },
- .eager => {},
- }
- switch (struct_type.layout) {
- .@"packed" => return .{
- .scalar = Type.fromInterned(struct_type.backingIntTypeUnordered(ip)).abiSize(zcu),
- },
- .auto, .@"extern" => {
- assert(struct_type.haveLayout(ip));
- return .{ .scalar = struct_type.sizeUnordered(ip) };
- },
- }
- },
- .tuple_type => |tuple| {
- switch (strat) {
- .sema => try ty.resolveLayout(strat.pt(zcu, tid)),
- .lazy, .eager => {},
- }
- const field_count = tuple.types.len;
- if (field_count == 0) {
- return .{ .scalar = 0 };
- }
- return .{ .scalar = ty.structFieldOffset(field_count, zcu) };
- },
-
- .union_type => {
- const union_type = ip.loadUnionType(ty.toIntern());
- switch (strat) {
- .sema => try ty.resolveLayout(strat.pt(zcu, tid)),
- .lazy => {
- const pt = strat.pt(zcu, tid);
- if (!union_type.flagsUnordered(ip).status.haveLayout()) return .{
- .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })),
- };
- },
- .eager => {},
- }
-
- assert(union_type.haveLayout(ip));
- return .{ .scalar = union_type.sizeUnordered(ip) };
- },
- .opaque_type => unreachable, // no size available
- .enum_type => return .{ .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiSize(zcu) },
-
- // values, not types
- .undef,
- .simple_value,
- .variable,
- .@"extern",
- .func,
- .int,
- .err,
- .error_union,
+ };
+ return ty.abiAlignment(zcu).forward(bytes);
+ },
+ .opt_type => |child_ty_ip| {
+ const child_ty: Type = .fromInterned(child_ty_ip);
+ if (child_ty.isNoReturn(zcu)) return 0;
+ const child_size = child_ty.abiSize(zcu);
+ if (ty.optionalReprIsPayload(zcu)) return child_size;
+ // Optional types are represented as a struct with the child type as the first
+ // field and a boolean as the second. Since the child type's abi alignment is
+ // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
+ // to the child type's ABI alignment.
+ return child_size + child_ty.abiAlignment(zcu).toByteUnits().?;
+ },
+ .error_set_type, .inferred_error_set_type => errorAbiSize(zcu),
+ .error_union_type => |error_union| {
+ const payload_ty: Type = .fromInterned(error_union.payload_type);
+ // This code needs to be kept in sync with the equivalent switch prong
+ // in abiAlignmentInner.
+ const code_size = errorAbiSize(zcu);
+ const code_align = errorAbiAlignment(zcu);
+ const payload_size = payload_ty.abiSize(zcu);
+ const payload_align = payload_ty.abiAlignment(zcu);
+ // The layout will either be (code, payload, padding) or (payload, code, padding)
+ // depending on which has larger alignment. So the overall size is just the code
+ // and payload sizes added and padded to the larger alignment.
+ const big_align = code_align.maxStrict(payload_align);
+ return big_align.forward(payload_size + code_size);
+ },
+ .func_type => 0,
+ .simple_type => |t| switch (t) {
+ .void,
+ .noreturn,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
.enum_literal,
- .enum_tag,
- .empty_enum_value,
- .float,
- .ptr,
- .slice,
- .opt,
- .aggregate,
- .un,
- // memoization, not types
- .memoized_call,
- => unreachable,
- },
- }
-}
-
-fn abiSizeInnerOptional(
- ty: Type,
- comptime strat: ResolveStratLazy,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!AbiSizeInner {
- const child_ty = ty.optionalChild(zcu);
+ => 0,
- if (child_ty.isNoReturn(zcu)) {
- return .{ .scalar = 0 };
- }
+ .bool => 1,
+ .anyerror, .adhoc_inferred_error_set => errorAbiSize(zcu),
+ .usize, .isize => ptrAbiSize(target),
- if (!(child_ty.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) {
- error.NeedLazy => if (strat == .lazy) {
- const pt = strat.pt(zcu, tid);
- return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })) };
- } else unreachable,
- else => |e| return e,
- })) return .{ .scalar = 1 };
+ .c_char => target.cTypeByteSize(.char),
+ .c_short => target.cTypeByteSize(.short),
+ .c_ushort => target.cTypeByteSize(.ushort),
+ .c_int => target.cTypeByteSize(.int),
+ .c_uint => target.cTypeByteSize(.uint),
+ .c_long => target.cTypeByteSize(.long),
+ .c_ulong => target.cTypeByteSize(.ulong),
+ .c_longlong => target.cTypeByteSize(.longlong),
+ .c_ulonglong => target.cTypeByteSize(.ulonglong),
+ .c_longdouble => target.cTypeByteSize(.longdouble),
- if (ty.optionalReprIsPayload(zcu)) {
- return child_ty.abiSizeInner(strat, zcu, tid);
- }
+ .f16 => 2,
+ .f32 => 4,
+ .f64 => 8,
+ .f80 => switch (target.cTypeBitSize(.longdouble)) {
+ 80 => target.cTypeByteSize(.longdouble),
+ else => Type.u80.abiSize(zcu),
+ },
+ .f128 => 16,
- const payload_size = switch (try child_ty.abiSizeInner(strat, zcu, tid)) {
- .scalar => |elem_size| elem_size,
- .val => switch (strat) {
- .sema => unreachable,
- .eager => unreachable,
- .lazy => return .{ .val = Value.fromInterned(try strat.pt(zcu, tid).intern(.{ .int = .{
- .ty = .comptime_int_type,
- .storage = .{ .lazy_size = ty.toIntern() },
- } })) },
+ .anyopaque => unreachable,
+ .generic_poison => unreachable,
+ },
+ .tuple_type => |tuple| ty.structFieldOffset(tuple.types.len, zcu),
+ .struct_type => {
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ switch (struct_obj.layout) {
+ .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiSize(zcu),
+ .auto, .@"extern" => return struct_obj.size,
+ }
+ },
+ .union_type => {
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ switch (union_obj.layout) {
+ .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiSize(zcu),
+ .auto, .@"extern" => return union_obj.size,
+ }
},
- };
+ .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu),
+ .opaque_type => unreachable,
- // Optional types are represented as a struct with the child type as the first
- // field and a boolean as the second. Since the child type's abi alignment is
- // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
- // to the child type's ABI alignment.
- return .{
- .scalar = (child_ty.abiAlignment(zcu).toByteUnits() orelse 0) + payload_size,
+ // values, not types
+ .undef,
+ .simple_value,
+ .variable,
+ .@"extern",
+ .func,
+ .int,
+ .err,
+ .error_union,
+ .enum_literal,
+ .enum_tag,
+ .empty_enum_value,
+ .float,
+ .ptr,
+ .slice,
+ .opt,
+ .aggregate,
+ .un,
+ // memoization, not types
+ .memoized_call,
+ => unreachable,
};
}
pub fn ptrAbiAlignment(target: *const Target) Alignment {
- return Alignment.fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8));
+ return .fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8));
+}
+pub fn ptrAbiSize(target: *const Target) u64 {
+ return @divExact(target.ptrBitWidth(), 8);
+}
+pub fn errorAbiAlignment(zcu: *const Zcu) Alignment {
+ return .fromNonzeroByteUnits(std.zig.target.intAlignment(zcu.getTarget(), zcu.errorSetBits()));
+}
+pub fn errorAbiSize(zcu: *const Zcu) u64 {
+ return std.zig.target.intByteSize(zcu.getTarget(), zcu.errorSetBits());
}
+/// Asserts that `ty` is not an opaque or comptime-only type.
+/// Once #19755 is implemented, this query will only work on types with a defined bit-level representation.
pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
- return bitSizeInner(ty, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {
- return bitSizeInner(ty, .sema, pt.zcu, pt.tid);
-}
-
-pub fn bitSizeInner(
- ty: Type,
- comptime strat: ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!u64 {
const target = zcu.getTarget();
const ip = &zcu.intern_pool;
-
- const strat_lazy: ResolveStratLazy = strat.toLazy();
-
- switch (ip.indexToKey(ty.toIntern())) {
- .int_type => |int_type| return int_type.bits,
+ assertHasLayout(ty, zcu);
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .int_type => |int_type| int_type.bits,
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
- .slice => return target.ptrBitWidth() * 2,
- else => return target.ptrBitWidth(),
+ .slice => target.ptrBitWidth() * 2,
+ else => target.ptrBitWidth(),
},
- .anyframe_type => return target.ptrBitWidth(),
-
+ .anyframe_type => target.ptrBitWidth(),
.array_type => |array_type| {
- const len = array_type.lenIncludingSentinel();
- if (len == 0) return 0;
const elem_ty: Type = .fromInterned(array_type.child);
- switch (zcu.comp.getZigBackend()) {
- else => {
- const elem_size = (try elem_ty.abiSizeInner(strat_lazy, zcu, tid)).scalar;
- if (elem_size == 0) return 0;
- const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid);
- return (len - 1) * 8 * elem_size + elem_bit_size;
+ const len = array_type.lenIncludingSentinel();
+ return switch (zcu.comp.getZigBackend()) {
+ .stage2_x86_64 => len * elem_ty.bitSize(zcu),
+ // this case will be removed under #19755
+ else => switch (len) {
+ 0 => 0,
+ else => (len - 1) * 8 * elem_ty.abiSize(zcu) + elem_ty.bitSize(zcu),
},
- .stage2_x86_64 => {
- const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid);
- return elem_bit_size * len;
- },
- }
- },
- .vector_type => |vector_type| {
- const child_ty: Type = .fromInterned(vector_type.child);
- const elem_bit_size = try child_ty.bitSizeInner(strat, zcu, tid);
- return elem_bit_size * vector_type.len;
- },
- .opt_type => {
- // Optionals and error unions are not packed so their bitsize
- // includes padding bits.
- return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8;
+ };
},
+ .vector_type => |vec| vec.len * Type.fromInterned(vec.child).bitSize(zcu),
+ .error_set_type, .inferred_error_set_type => zcu.errorSetBits(),
+ .func_type => unreachable,
- .error_set_type, .inferred_error_set_type => return zcu.errorSetBits(),
-
- .error_union_type => {
- // Optionals and error unions are not packed so their bitsize
- // includes padding bits.
- return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8;
- },
- .func_type => unreachable, // represents machine code; not a pointer
.simple_type => |t| switch (t) {
- .f16 => return 16,
- .f32 => return 32,
- .f64 => return 64,
- .f80 => return 80,
- .f128 => return 128,
+ .void => 0,
+ .bool => 1,
+ .anyerror, .adhoc_inferred_error_set => zcu.errorSetBits(),
+ .usize, .isize => target.ptrBitWidth(),
- .usize,
- .isize,
- => return target.ptrBitWidth(),
+ .c_char => target.cTypeBitSize(.char),
+ .c_short => target.cTypeBitSize(.short),
+ .c_ushort => target.cTypeBitSize(.ushort),
+ .c_int => target.cTypeBitSize(.int),
+ .c_uint => target.cTypeBitSize(.uint),
+ .c_long => target.cTypeBitSize(.long),
+ .c_ulong => target.cTypeBitSize(.ulong),
+ .c_longlong => target.cTypeBitSize(.longlong),
+ .c_ulonglong => target.cTypeBitSize(.ulonglong),
+ .c_longdouble => target.cTypeBitSize(.longdouble),
- .c_char => return target.cTypeBitSize(.char),
- .c_short => return target.cTypeBitSize(.short),
- .c_ushort => return target.cTypeBitSize(.ushort),
- .c_int => return target.cTypeBitSize(.int),
- .c_uint => return target.cTypeBitSize(.uint),
- .c_long => return target.cTypeBitSize(.long),
- .c_ulong => return target.cTypeBitSize(.ulong),
- .c_longlong => return target.cTypeBitSize(.longlong),
- .c_ulonglong => return target.cTypeBitSize(.ulonglong),
- .c_longdouble => return target.cTypeBitSize(.longdouble),
-
- .bool => return 1,
- .void => return 0,
-
- .anyerror,
- .adhoc_inferred_error_set,
- => return zcu.errorSetBits(),
+ .f16 => 16,
+ .f32 => 32,
+ .f64 => 64,
+ .f80 => 80,
+ .f128 => 128,
.anyopaque => unreachable,
.type => unreachable,
@@ -1717,49 +1062,30 @@ pub fn bitSizeInner(
.enum_literal => unreachable,
.generic_poison => unreachable,
},
+
.struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- const is_packed = struct_type.layout == .@"packed";
- if (strat == .sema) {
- const pt = strat.pt(zcu, tid);
- try ty.resolveFields(pt);
- if (is_packed) try ty.resolveLayout(pt);
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ switch (struct_obj.layout) {
+ .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).bitSize(zcu),
+ .auto, .@"extern" => return struct_obj.size * 8, // will be `unreachable` under #19755
}
- if (is_packed) {
- return try Type.fromInterned(struct_type.backingIntTypeUnordered(ip))
- .bitSizeInner(strat, zcu, tid);
- }
- return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8;
- },
-
- .tuple_type => {
- return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8;
},
-
.union_type => {
- const union_type = ip.loadUnionType(ty.toIntern());
- const is_packed = ty.containerLayout(zcu) == .@"packed";
- if (strat == .sema) {
- const pt = strat.pt(zcu, tid);
- try ty.resolveFields(pt);
- if (is_packed) try ty.resolveLayout(pt);
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ switch (union_obj.layout) {
+ .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).bitSize(zcu),
+ .auto, .@"extern" => return union_obj.size * 8, // will be `unreachable` under #19755
}
- if (!is_packed) {
- return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8;
- }
- assert(union_type.flagsUnordered(ip).status.haveFieldTypes());
-
- var size: u64 = 0;
- for (0..union_type.field_types.len) |field_index| {
- const field_ty = union_type.field_types.get(ip)[field_index];
- size = @max(size, try Type.fromInterned(field_ty).bitSizeInner(strat, zcu, tid));
- }
-
- return size;
},
+ .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).bitSize(zcu),
+
+ // will be `unreachable` under #19755
+ .opt_type,
+ .error_union_type,
+ .tuple_type,
+ => ty.abiSize(zcu) * 8,
+
.opaque_type => unreachable,
- .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty)
- .bitSizeInner(strat, zcu, tid),
// values, not types
.undef,
@@ -1782,23 +1108,6 @@ pub fn bitSizeInner(
// memoization, not types
.memoized_call,
=> unreachable,
- }
-}
-
-/// Returns true if the type's layout is already resolved and it is safe
-/// to use `abiSize`, `abiAlignment` and `bitSize` on it.
-pub fn layoutIsResolved(ty: Type, zcu: *const Zcu) bool {
- const ip = &zcu.intern_pool;
- return switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => ip.loadStructType(ty.toIntern()).haveLayout(ip),
- .union_type => ip.loadUnionType(ty.toIntern()).haveLayout(ip),
- .array_type => |array_type| {
- if (array_type.lenIncludingSentinel() == 0) return true;
- return Type.fromInterned(array_type.child).layoutIsResolved(zcu);
- },
- .opt_type => |child| Type.fromInterned(child).layoutIsResolved(zcu),
- .error_union_type => |k| Type.fromInterned(k.payload_type).layoutIsResolved(zcu),
- else => true,
};
}
@@ -1841,7 +1150,7 @@ pub fn isSliceAtRuntime(ty: Type, zcu: *const Zcu) bool {
}
pub fn slicePtrFieldType(ty: Type, zcu: *const Zcu) Type {
- return Type.fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern()));
+ return .fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern()));
}
pub fn isConstPtr(ty: Type, zcu: *const Zcu) bool {
@@ -1897,10 +1206,7 @@ pub fn isPtrAtRuntime(ty: Type, zcu: *const Zcu) bool {
/// For pointer-like optionals, returns true, otherwise returns the allowzero property
/// of pointers.
pub fn ptrAllowsZero(ty: Type, zcu: *const Zcu) bool {
- if (ty.isPtrLikeOptional(zcu)) {
- return true;
- }
- return ty.ptrInfo(zcu).flags.is_allowzero;
+ return ty.isPtrLikeOptional(zcu) or ty.ptrInfo(zcu).flags.is_allowzero;
}
/// See also `isPtrLikeOptional`.
@@ -1918,7 +1224,6 @@ pub fn optionalReprIsPayload(ty: Type, zcu: *const Zcu) bool {
/// Returns true if the type is optional and would be lowered to a single pointer
/// address value, using 0 for null. Note that this returns true for C pointers.
-/// This function must be kept in sync with `Sema.typePtrOrOptionalPtrTy`.
pub fn isPtrLikeOptional(ty: Type, zcu: *const Zcu) bool {
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
.ptr_type => |ptr_type| ptr_type.flags.size == .c,
@@ -1947,52 +1252,75 @@ pub fn childTypeIp(ty: Type, ip: *const InternPool) Type {
return Type.fromInterned(ip.childType(ty.toIntern()));
}
-/// For `*[N]T`, returns `T`.
-/// For `?*T`, returns `T`.
-/// For `?*[N]T`, returns `T`.
-/// For `?[*]T`, returns `T`.
-/// For `*T`, returns `T`.
-/// For `[*]T`, returns `T`.
-/// For `[N]T`, returns `T`.
-/// For `[]T`, returns `T`.
-/// For `anyframe->T`, returns `T`.
-pub fn elemType2(ty: Type, zcu: *const Zcu) Type {
- return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
- .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
- .one => Type.fromInterned(ptr_type.child).shallowElemType(zcu),
- .many, .c, .slice => Type.fromInterned(ptr_type.child),
+/// Similar to `childType`, but for pointer-like (or slice-like) optionals, gets the child type
+/// of the *pointer* type. Asserts that `ty` is either a pointer or a pointer-like optional.
+///
+/// Essentially, unwraps any one of the following into `T`:
+/// ```
+/// *T ?*T *allowzero T
+/// [*]T ?[*]T [*]allowzero T
+/// []T ?[]T []allowzero T
+/// [*c]T
+/// ```
+/// This is primarily useful in Sema to implement operations which can act on optional pointers.
+pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type {
+ switch (ty.zigTypeTag(zcu)) {
+ .pointer => return ty.childType(zcu),
+ .optional => {
+ const ptr_ty = ty.childType(zcu);
+ const ptr_info = zcu.intern_pool.indexToKey(ptr_ty.toIntern()).ptr_type;
+ assert(ptr_info.flags.size != .c);
+ assert(!ptr_info.flags.is_allowzero);
+ return .fromInterned(ptr_info.child);
},
- .anyframe_type => |child| {
- assert(child != .none);
- return Type.fromInterned(child);
- },
- .vector_type => |vector_type| Type.fromInterned(vector_type.child),
- .array_type => |array_type| Type.fromInterned(array_type.child),
- .opt_type => |child| Type.fromInterned(zcu.intern_pool.childType(child)),
else => unreachable,
- };
+ }
}
/// Given that `ty` is an indexable pointer, returns its element type. Specifically:
/// * for `*[n]T`, returns `T`
+/// * for `*@Vector(n, T)`, returns `T`
/// * for `[]T`, returns `T`
/// * for `[*]T`, returns `T`
/// * for `[*c]T`, returns `T`
+///
+/// Tuples are not supported because they do not have a single element type.
+///
+/// MLUGG TODO: should i even have this one? it's a subset of indexableElem
pub fn indexablePtrElem(ty: Type, zcu: *const Zcu) Type {
const ip = &zcu.intern_pool;
const ptr_type = ip.indexToKey(ty.toIntern()).ptr_type;
- switch (ptr_type.flags.size) {
+ return switch (ptr_type.flags.size) {
.many, .slice, .c => return .fromInterned(ptr_type.child),
- .one => {},
- }
- const array_type = ip.indexToKey(ptr_type.child).array_type;
- return .fromInterned(array_type.child);
+ .one => switch (ip.indexToKey(ptr_type.child)) {
+ inline .array_type, .vector_type => |arr| return .fromInterned(arr.child),
+ else => unreachable,
+ },
+ };
}
-fn shallowElemType(child_ty: Type, zcu: *const Zcu) Type {
- return switch (child_ty.zigTypeTag(zcu)) {
- .array, .vector => child_ty.childType(zcu),
- else => child_ty,
+/// Given that `ty` is an indexable type, returns its element type. Specifically:
+/// * for `[n]T`, returns `T`
+/// * for `@Vector(n, T)`, returns `T`
+/// * for `*[n]T`, returns `T`
+/// * for `*@Vector(n, T)`, returns `T`
+/// * for `[]T`, returns `T`
+/// * for `[*]T`, returns `T`
+/// * for `[*c]T`, returns `T`
+///
+/// Tuples are not supported because they do not have a single element type.
+pub fn indexableElem(ty: Type, zcu: *const Zcu) Type {
+ const ip = &zcu.intern_pool;
+ return switch (ip.indexToKey(ty.toIntern())) {
+ inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
+ .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
+ .many, .slice, .c => .fromInterned(ptr_type.child),
+ .one => switch (ip.indexToKey(ptr_type.child)) {
+ inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
+ else => unreachable,
+ },
+ },
+ else => unreachable,
};
}
@@ -2004,17 +1332,17 @@ pub fn scalarType(ty: Type, zcu: *const Zcu) Type {
};
}
-/// Asserts that the type is an optional.
-/// Note that for C pointers this returns the type unmodified.
+/// Asserts that the type is an optional, or a C pointer.
+/// For C pointers this returns the type unmodified.
pub fn optionalChild(ty: Type, zcu: *const Zcu) Type {
- return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
- .opt_type => |child| Type.fromInterned(child),
- .ptr_type => |ptr_type| b: {
+ switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
+ .opt_type => |child| return .fromInterned(child),
+ .ptr_type => |ptr_type| {
assert(ptr_type.flags.size == .c);
- break :b ty;
+ return ty;
},
else => unreachable,
- };
+ }
}
/// Returns the tag type of a union, if the type is a union and it has a tag type.
@@ -2025,15 +1353,11 @@ pub fn unionTagType(ty: Type, zcu: *const Zcu) ?Type {
.union_type => {},
else => return null,
}
- const union_type = ip.loadUnionType(ty.toIntern());
- const union_flags = union_type.flagsUnordered(ip);
- switch (union_flags.runtime_tag) {
- .tagged => {
- assert(union_flags.status.haveFieldTypes());
- return Type.fromInterned(union_type.enum_tag_ty);
- },
- else => return null,
- }
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ return switch (union_obj.runtime_tag) {
+ .tagged => .fromInterned(union_obj.enum_tag_type),
+ .none, .safety => null,
+ };
}
/// Same as `unionTagType` but includes safety tag.
@@ -2043,9 +1367,8 @@ pub fn unionTagTypeSafety(ty: Type, zcu: *const Zcu) ?Type {
return switch (ip.indexToKey(ty.toIntern())) {
.union_type => {
const union_type = ip.loadUnionType(ty.toIntern());
- if (!union_type.hasTag(ip)) return null;
- assert(union_type.haveFieldTypes(ip));
- return Type.fromInterned(union_type.enum_tag_ty);
+ if (union_type.runtime_tag == .none) return null;
+ return Type.fromInterned(union_type.enum_tag_type);
},
else => null,
};
@@ -2055,7 +1378,7 @@ pub fn unionTagTypeSafety(ty: Type, zcu: *const Zcu) ?Type {
/// not be stored at runtime.
pub fn unionTagTypeHypothetical(ty: Type, zcu: *const Zcu) Type {
const union_obj = zcu.typeToUnion(ty).?;
- return Type.fromInterned(union_obj.enum_tag_ty);
+ return Type.fromInterned(union_obj.enum_tag_type);
}
pub fn unionFieldType(ty: Type, enum_tag: Value, zcu: *const Zcu) ?Type {
@@ -2105,9 +1428,9 @@ pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
pub fn containerLayout(ty: Type, zcu: *const Zcu) std.builtin.Type.ContainerLayout {
const ip = &zcu.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => ip.loadStructType(ty.toIntern()).layout,
.tuple_type => .auto,
- .union_type => ip.loadUnionType(ty.toIntern()).flagsUnordered(ip).layout,
+ .struct_type => ip.loadStructType(ty.toIntern()).layout,
+ .union_type => ip.loadUnionType(ty.toIntern()).layout,
else => unreachable,
};
}
@@ -2182,33 +1505,6 @@ pub fn errorSetHasFieldIp(
};
}
-/// Returns whether ty, which must be an error set, includes an error `name`.
-/// Might return a false negative if `ty` is an inferred error set and not fully
-/// resolved yet.
-pub fn errorSetHasField(ty: Type, name: []const u8, zcu: *const Zcu) bool {
- const ip = &zcu.intern_pool;
- return switch (ty.toIntern()) {
- .anyerror_type => true,
- else => switch (ip.indexToKey(ty.toIntern())) {
- .error_set_type => |error_set_type| {
- // If the string is not interned, then the field certainly is not present.
- const field_name_interned = ip.getString(name).unwrap() orelse return false;
- return error_set_type.nameIndex(ip, field_name_interned) != null;
- },
- .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
- .anyerror_type => true,
- .none => false,
- else => |t| {
- // If the string is not interned, then the field certainly is not present.
- const field_name_interned = ip.getString(name).unwrap() orelse return false;
- return ip.indexToKey(t).error_set_type.nameIndex(ip, field_name_interned) != null;
- },
- },
- else => unreachable,
- },
- };
-}
-
/// Asserts the type is an array or vector or struct.
pub fn arrayLen(ty: Type, zcu: *const Zcu) u64 {
return ty.arrayLenIp(&zcu.intern_pool);
@@ -2308,8 +1604,12 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
.c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong) },
else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| return int_type,
- .struct_type => ty = Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntTypeUnordered(ip)),
- .enum_type => ty = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty),
+ .struct_type => {
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ assert(struct_obj.layout == .@"packed");
+ ty = .fromInterned(struct_obj.packed_backing_int_type);
+ },
+ .enum_type => ty = .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
.vector_type => |vector_type| ty = Type.fromInterned(vector_type.child),
.error_set_type, .inferred_error_set_type => {
@@ -2355,25 +1655,6 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
};
}
-pub fn isNamedInt(ty: Type) bool {
- return switch (ty.toIntern()) {
- .usize_type,
- .isize_type,
- .c_char_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- => true,
-
- else => false,
- };
-}
-
/// Returns `false` for `comptime_float`.
pub fn isRuntimeFloat(ty: Type) bool {
return switch (ty.toIntern()) {
@@ -2488,17 +1769,16 @@ pub fn isNumeric(ty: Type, zcu: *const Zcu) bool {
};
}
-/// During semantic analysis, instead call `Sema.typeHasOnePossibleValue` which
-/// resolves field types rather than asserting they are already resolved.
+/// MLUGG TODO: deal with our friends structs and unions
pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
- const io = comp.io;
const ip = &zcu.intern_pool;
+ assertHasLayout(starting_type, zcu);
var ty = starting_type;
while (true) switch (ty.toIntern()) {
- .empty_tuple_type => return Value.empty_tuple,
+ .empty_tuple_type => return .empty_tuple,
else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| {
@@ -2563,31 +1843,37 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
.adhoc_inferred_error_set,
=> return null,
- .void => return Value.void,
- .noreturn => return Value.@"unreachable",
- .null => return Value.null,
- .undefined => return Value.undef,
+ .void => return .void,
+ .noreturn => return .@"unreachable",
+ .null => return .null,
+ .undefined => return .undef,
.generic_poison => unreachable,
},
.struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- assert(struct_type.haveFieldTypes(ip));
- if (struct_type.knownNonOpv(ip))
- return null;
- const field_vals = try zcu.gpa.alloc(InternPool.Index, struct_type.field_types.len);
- defer zcu.gpa.free(field_vals);
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ if (struct_obj.layout == .@"packed") {
+ const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
+ const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
+ _ = backing_val; // MLUGG TODO: represent unions as their bits!
+ } else {
+ if (!struct_obj.has_one_possible_value) return null;
+ }
+ // There is an OPV.
+ const field_vals = try gpa.alloc(InternPool.Index, struct_obj.field_types.len);
+ defer gpa.free(field_vals);
for (field_vals, 0..) |*field_val, i_usize| {
const i: u32 = @intCast(i_usize);
- if (struct_type.fieldIsComptime(ip, i)) {
- assert(struct_type.haveFieldInits(ip));
- field_val.* = struct_type.field_inits.get(ip)[i];
+ if (struct_obj.field_is_comptime_bits.get(ip, i)) {
+ // MLUGG TODO: this is kinda a problem... we don't necessarily know the opv field vals!
+ // for now i'm just not letting structs with comptime fields be opv :)
+ if (true) return null;
+ assertHasInits(ty, zcu);
+ field_val.* = struct_obj.field_defaults.get(ip)[i];
continue;
}
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
- if (try field_ty.onePossibleValue(pt)) |field_opv| {
- field_val.* = field_opv.toIntern();
- } else return null;
+ const field_ty = Type.fromInterned(struct_obj.field_types.get(ip)[i]);
+ field_val.* = (try field_ty.onePossibleValue(pt)).?.toIntern();
}
// In this case the struct has no runtime-known fields and
@@ -2623,12 +1909,13 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
},
.union_type => {
+ // MLUGG TODO: is this nonsensical or what!!!!!!
const union_obj = ip.loadUnionType(ty.toIntern());
- const tag_val = (try Type.fromInterned(union_obj.enum_tag_ty).onePossibleValue(pt)) orelse
+ const tag_val = (try Type.fromInterned(union_obj.enum_tag_type).onePossibleValue(pt)) orelse
return null;
if (union_obj.field_types.len == 0) {
const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() });
- return Value.fromInterned(only);
+ return .fromInterned(only);
}
const only_field_ty = union_obj.field_types.get(ip)[0];
const val_val = (try Type.fromInterned(only_field_ty).onePossibleValue(pt)) orelse
@@ -2638,47 +1925,34 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
.tag = tag_val.toIntern(),
.val = val_val.toIntern(),
});
- return Value.fromInterned(only);
+ return .fromInterned(only);
},
.opaque_type => return null,
.enum_type => {
- const enum_type = ip.loadEnumType(ty.toIntern());
- switch (enum_type.tag_mode) {
- .nonexhaustive => {
- if (enum_type.tag_ty == .comptime_int_type) return null;
-
- if (try Type.fromInterned(enum_type.tag_ty).onePossibleValue(pt)) |int_opv| {
- const only = try pt.intern(.{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = int_opv.toIntern(),
- } });
- return Value.fromInterned(only);
- }
-
- return null;
- },
- .auto, .explicit => {
- if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null;
-
- return Value.fromInterned(switch (enum_type.names.len) {
- 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }),
- 1 => try pt.intern(.{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = if (enum_type.values.len == 0)
- (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
- else
- try ip.getCoercedInts(
- gpa,
- io,
- pt.tid,
- ip.indexToKey(enum_type.values.get(ip)[0]).int,
- enum_type.tag_ty,
- ),
- } }),
- else => return null,
- });
- },
+ const enum_obj = ip.loadEnumType(ty.toIntern());
+ if (enum_obj.nonexhaustive) {
+ const int_opv = try Type.fromInterned(enum_obj.int_tag_type).onePossibleValue(pt) orelse return null;
+ return .fromInterned(try pt.intern(.{ .enum_tag = .{
+ .ty = ty.toIntern(),
+ .int = int_opv.toIntern(),
+ } }));
}
+ // MLUGG TODO: this is to preserve existing semantics, i REALLY don't fuck with it...
+ if (enum_obj.int_tag_type == .comptime_int_type) {
+ return switch (enum_obj.field_names.len) {
+ 0 => .fromInterned(try pt.intern(.{ .empty_enum_value = ty.toIntern() })),
+ 1 => try pt.enumValueFieldIndex(ty, 0),
+ else => null,
+ };
+ }
+ const int_tag_opv = try Type.fromInterned(enum_obj.int_tag_type).onePossibleValue(pt) orelse return null;
+ if (enum_obj.field_names.len == 0) {
+ return .fromInterned(try pt.intern(.{ .empty_enum_value = ty.toIntern() }));
+ }
+ return .fromInterned(try pt.intern(.{ .enum_tag = .{
+ .ty = ty.toIntern(),
+ .int = int_tag_opv.toIntern(),
+ } }));
},
// values, not types
@@ -2706,211 +1980,106 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
};
}
-/// During semantic analysis, instead call `ty.comptimeOnlySema` which
-/// resolves field types rather than asserting they are already resolved.
+/// Asserts that `ty` has its layout resolved. `generic_poison` will return `false`.
pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
- return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable;
-}
-
-pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
- return try ty.comptimeOnlyInner(.sema, pt.zcu, pt.tid);
-}
-
-/// `generic_poison` will return false.
-/// May return false negatives when structs and unions are having their field types resolved.
-pub fn comptimeOnlyInner(
- ty: Type,
- comptime strat: ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!bool {
const ip = &zcu.intern_pool;
- const io = zcu.comp.io;
- return switch (ty.toIntern()) {
- .empty_tuple_type => false,
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .array_type => |array_type| return Type.fromInterned(array_type.child).comptimeOnly(zcu),
+ .vector_type => |vector_type| return Type.fromInterned(vector_type.child).comptimeOnly(zcu),
+ .opt_type => |child| return Type.fromInterned(child).comptimeOnly(zcu),
+ .error_union_type => |error_union_type| return Type.fromInterned(error_union_type.payload_type).comptimeOnly(zcu),
+ .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).comptimeOnly(zcu),
- else => switch (ip.indexToKey(ty.toIntern())) {
- .int_type => false,
- .ptr_type => |ptr_type| {
- const child_ty = Type.fromInterned(ptr_type.child);
- switch (child_ty.zigTypeTag(zcu)) {
- .@"fn" => return !try child_ty.fnHasRuntimeBitsInner(strat, zcu, tid),
- .@"opaque" => return false,
- else => return child_ty.comptimeOnlyInner(strat, zcu, tid),
- }
- },
- .anyframe_type => |child| {
- if (child == .none) return false;
- return Type.fromInterned(child).comptimeOnlyInner(strat, zcu, tid);
- },
- .array_type => |array_type| return Type.fromInterned(array_type.child).comptimeOnlyInner(strat, zcu, tid),
- .vector_type => |vector_type| return Type.fromInterned(vector_type.child).comptimeOnlyInner(strat, zcu, tid),
- .opt_type => |child| return Type.fromInterned(child).comptimeOnlyInner(strat, zcu, tid),
- .error_union_type => |error_union_type| return Type.fromInterned(error_union_type.payload_type).comptimeOnlyInner(strat, zcu, tid),
+ .int_type,
+ .ptr_type,
+ .anyframe_type,
+ .error_set_type,
+ .inferred_error_set_type,
+ .opaque_type,
+ => false,
- .error_set_type,
- .inferred_error_set_type,
+ // These are function bodies, not function pointers.
+ .func_type => true,
+
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .anyopaque,
+ .bool,
+ .void,
+ .anyerror,
+ .adhoc_inferred_error_set,
+ .noreturn,
+ .generic_poison,
=> false,
- // These are function bodies, not function pointers.
- .func_type => true,
-
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .anyopaque,
- .bool,
- .void,
- .anyerror,
- .adhoc_inferred_error_set,
- .noreturn,
- .generic_poison,
- => false,
-
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- => true,
- },
- .struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- // packed structs cannot be comptime-only because they have a well-defined
- // memory layout and every field has a well-defined bit pattern.
- if (struct_type.layout == .@"packed")
- return false;
-
- return switch (strat) {
- .normal => switch (struct_type.requiresComptime(ip)) {
- .wip => unreachable,
- .no => false,
- .yes => true,
- .unknown => unreachable,
- },
- .sema => switch (struct_type.setRequiresComptimeWip(ip, io)) {
- .no, .wip => false,
- .yes => true,
- .unknown => {
- if (struct_type.flagsUnordered(ip).field_types_wip) {
- struct_type.setRequiresComptime(ip, io, .unknown);
- return false;
- }
-
- errdefer struct_type.setRequiresComptime(ip, io, .unknown);
-
- const pt = strat.pt(zcu, tid);
- try ty.resolveFields(pt);
-
- for (0..struct_type.field_types.len) |i_usize| {
- const i: u32 = @intCast(i_usize);
- if (struct_type.fieldIsComptime(ip, i)) continue;
- const field_ty = struct_type.field_types.get(ip)[i];
- if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
- // Note that this does not cause the layout to
- // be considered resolved. Comptime-only types
- // still maintain a layout of their
- // runtime-known fields.
- struct_type.setRequiresComptime(ip, io, .yes);
- return true;
- }
- }
-
- struct_type.setRequiresComptime(ip, io, .no);
- return false;
- },
- },
- };
- },
-
- .tuple_type => |tuple| {
- for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
- const have_comptime_val = val != .none;
- if (!have_comptime_val and try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) return true;
- }
- return false;
- },
-
- .union_type => {
- const union_type = ip.loadUnionType(ty.toIntern());
- return switch (strat) {
- .normal => switch (union_type.requiresComptime(ip)) {
- .wip => unreachable,
- .no => false,
- .yes => true,
- .unknown => unreachable,
- },
- .sema => switch (union_type.setRequiresComptimeWip(ip, io)) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- if (union_type.flagsUnordered(ip).status == .field_types_wip) {
- union_type.setRequiresComptime(ip, io, .unknown);
- return false;
- }
-
- errdefer union_type.setRequiresComptime(ip, io, .unknown);
-
- const pt = strat.pt(zcu, tid);
- try ty.resolveFields(pt);
-
- for (0..union_type.field_types.len) |field_idx| {
- const field_ty = union_type.field_types.get(ip)[field_idx];
- if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
- union_type.setRequiresComptime(ip, io, .yes);
- return true;
- }
- }
-
- union_type.setRequiresComptime(ip, io, .no);
- return false;
- },
- },
- };
- },
-
- .opaque_type => false,
-
- .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).comptimeOnlyInner(strat, zcu, tid),
-
- // values, not types
- .undef,
- .simple_value,
- .variable,
- .@"extern",
- .func,
- .int,
- .err,
- .error_union,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
.enum_literal,
- .enum_tag,
- .empty_enum_value,
- .float,
- .ptr,
- .slice,
- .opt,
- .aggregate,
- .un,
- // memoization, not types
- .memoized_call,
- => unreachable,
+ => true,
},
+ .struct_type => {
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ return switch (struct_obj.layout) {
+ .@"packed" => false,
+ .auto, .@"extern" => struct_obj.comptime_only,
+ };
+ },
+ .union_type => {
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ return switch (union_obj.layout) {
+ .@"packed" => false,
+ .auto, .@"extern" => union_obj.comptime_only,
+ };
+ },
+ .tuple_type => |tuple| {
+ for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
+ if (val != .none) continue;
+ if (!Type.fromInterned(field_ty).comptimeOnly(zcu)) continue;
+ return true;
+ }
+ return false;
+ },
+
+ // values, not types
+ .undef,
+ .simple_value,
+ .variable,
+ .@"extern",
+ .func,
+ .int,
+ .err,
+ .error_union,
+ .enum_literal,
+ .enum_tag,
+ .empty_enum_value,
+ .float,
+ .ptr,
+ .slice,
+ .opt,
+ .aggregate,
+ .un,
+ // memoization, not types
+ .memoized_call,
+ => unreachable,
};
}
@@ -3056,20 +2225,18 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
/// Asserts the type is an enum or a union.
pub fn intTagType(ty: Type, zcu: *const Zcu) Type {
const ip = &zcu.intern_pool;
- return switch (ip.indexToKey(ty.toIntern())) {
- .union_type => Type.fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_ty).intTagType(zcu),
- .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty),
+ const enum_ty: Type = switch (ip.indexToKey(ty.toIntern())) {
+ .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_type),
+ .enum_type => ty,
else => unreachable,
};
+ return .fromInterned(ip.loadEnumType(enum_ty.toIntern()).int_tag_type);
}
pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {
const ip = &zcu.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
- .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) {
- .nonexhaustive => true,
- .auto, .explicit => false,
- },
+ .enum_type => ip.loadEnumType(ty.toIntern()).nonexhaustive,
else => false,
};
}
@@ -3090,16 +2257,16 @@ pub fn errorSetNames(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.
}
pub fn enumFields(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.Slice {
- return zcu.intern_pool.loadEnumType(ty.toIntern()).names;
+ return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names;
}
pub fn enumFieldCount(ty: Type, zcu: *const Zcu) usize {
- return zcu.intern_pool.loadEnumType(ty.toIntern()).names.len;
+ return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names.len;
}
pub fn enumFieldName(ty: Type, field_index: usize, zcu: *const Zcu) InternPool.NullTerminatedString {
const ip = &zcu.intern_pool;
- return ip.loadEnumType(ty.toIntern()).names.get(ip)[field_index];
+ return ip.loadEnumType(ty.toIntern()).field_names.get(ip)[field_index];
}
pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, zcu: *const Zcu) ?u32 {
@@ -3119,7 +2286,7 @@ pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 {
.enum_tag => |info| info.int,
else => unreachable,
};
- assert(ip.typeOf(int_tag) == enum_type.tag_ty);
+ assert(ip.typeOf(int_tag) == enum_type.int_tag_type);
return enum_type.tagValueIndex(ip, int_tag);
}
@@ -3127,7 +2294,7 @@ pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 {
pub fn structFieldName(ty: Type, index: usize, zcu: *const Zcu) InternPool.OptionalNullTerminatedString {
const ip = &zcu.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => ip.loadStructType(ty.toIntern()).fieldName(ip, index).toOptional(),
+ .struct_type => ip.loadStructType(ty.toIntern()).field_names.get(ip)[index].toOptional(),
.tuple_type => .none,
else => unreachable,
};
@@ -3145,174 +2312,95 @@ pub fn structFieldCount(ty: Type, zcu: *const Zcu) u32 {
/// Returns the field type. Supports structs and unions.
pub fn fieldType(ty: Type, index: usize, zcu: *const Zcu) Type {
const ip = &zcu.intern_pool;
- return switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => Type.fromInterned(ip.loadStructType(ty.toIntern()).field_types.get(ip)[index]),
- .union_type => {
- const union_obj = ip.loadUnionType(ty.toIntern());
- return Type.fromInterned(union_obj.field_types.get(ip)[index]);
- },
- .tuple_type => |tuple| Type.fromInterned(tuple.types.get(ip)[index]),
+ const types = switch (ip.indexToKey(ty.toIntern())) {
+ .struct_type => ip.loadStructType(ty.toIntern()).field_types,
+ .union_type => ip.loadUnionType(ty.toIntern()).field_types,
+ .tuple_type => |tuple| tuple.types,
else => unreachable,
};
+ return .fromInterned(types.get(ip)[index]);
}
-pub fn fieldAlignment(ty: Type, index: usize, zcu: *Zcu) Alignment {
- return ty.fieldAlignmentInner(index, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn fieldAlignmentSema(ty: Type, index: usize, pt: Zcu.PerThread) SemaError!Alignment {
- return try ty.fieldAlignmentInner(index, .sema, pt.zcu, pt.tid);
-}
+// TODO MLUGG: clean up doc comments and usages of `{resolved,explicit}FieldAlignment`
-/// Returns the field alignment. Supports structs and unions.
-/// If `strat` is `.sema`, may perform type resolution.
-/// Asserts the layout is not packed.
-///
-/// Provide the struct field as the `ty`.
-pub fn fieldAlignmentInner(
- ty: Type,
- index: usize,
- comptime strat: ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!Alignment {
- const ip = &zcu.intern_pool;
- switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- assert(struct_type.layout != .@"packed");
- const explicit_align = struct_type.fieldAlign(ip, index);
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]);
- return field_ty.structFieldAlignmentInner(explicit_align, struct_type.layout, strat, zcu, tid);
- },
- .tuple_type => |tuple| {
- return (try Type.fromInterned(tuple.types.get(ip)[index]).abiAlignmentInner(
- strat.toLazy(),
- zcu,
- tid,
- )).scalar;
- },
- .union_type => {
- const union_obj = ip.loadUnionType(ty.toIntern());
- const layout = union_obj.flagsUnordered(ip).layout;
- assert(layout != .@"packed");
- const explicit_align = union_obj.fieldAlign(ip, index);
- const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[index]);
- return field_ty.unionFieldAlignmentInner(explicit_align, layout, strat, zcu, tid);
- },
- else => unreachable,
+/// 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 => {
+ 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 => {
+ 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,
+ };
}
-/// Returns the alignment of a non-packed struct field. Assert the layout is not packed.
-///
-/// Asserts that all resolution needed was done.
-pub fn structFieldAlignment(
+pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
+ const ip = &zcu.intern_pool;
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .tuple_type => .none,
+ .struct_type => {
+ const struct_obj = ip.loadStructType(ty.toIntern());
+ assert(struct_obj.layout != .@"packed");
+ if (struct_obj.field_aligns.len == 0) return .none;
+ return struct_obj.field_aligns.get(ip)[index];
+ },
+ .union_type => {
+ const union_obj = ip.loadUnionType(ty.toIntern());
+ assert(union_obj.layout != .@"packed");
+ if (union_obj.field_aligns.len == 0) return .none;
+ return union_obj.field_aligns.get(ip)[index];
+ },
+ else => unreachable,
+ };
+}
+
+/// Returns the alignment a struct field will have if not explicitly specified.
+/// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`.
+pub fn defaultStructFieldAlignment(
field_ty: Type,
- explicit_alignment: InternPool.Alignment,
layout: std.builtin.Type.ContainerLayout,
- zcu: *Zcu,
+ zcu: *const Zcu,
) Alignment {
- return field_ty.structFieldAlignmentInner(
- explicit_alignment,
- layout,
- .normal,
- zcu,
- {},
- ) catch unreachable;
-}
-
-/// Returns the alignment of a non-packed struct field. Assert the layout is not packed.
-/// May do type resolution when needed.
-/// Asserts that all resolution needed was done.
-pub fn structFieldAlignmentSema(
- field_ty: Type,
- explicit_alignment: InternPool.Alignment,
- layout: std.builtin.Type.ContainerLayout,
- pt: Zcu.PerThread,
-) SemaError!Alignment {
- return try field_ty.structFieldAlignmentInner(
- explicit_alignment,
- layout,
- .sema,
- pt.zcu,
- pt.tid,
- );
-}
-
-/// Returns the alignment of a non-packed struct field. Asserts the layout is not packed.
-/// If `strat` is `.sema`, may perform type resolution.
-pub fn structFieldAlignmentInner(
- field_ty: Type,
- explicit_alignment: Alignment,
- layout: std.builtin.Type.ContainerLayout,
- comptime strat: Type.ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!Alignment {
- assert(layout != .@"packed");
- if (explicit_alignment != .none) return explicit_alignment;
- const ty_abi_align = (try field_ty.abiAlignmentInner(
- strat.toLazy(),
- zcu,
- tid,
- )).scalar;
- switch (layout) {
+ const overalign_big_int = switch (layout) {
.@"packed" => unreachable,
- .auto => if (zcu.getTarget().ofmt != .c) return ty_abi_align,
- .@"extern" => {},
+ .auto => zcu.getTarget().ofmt == .c,
+ .@"extern" => true,
+ };
+ const abi_align = field_ty.abiAlignment(zcu);
+ assert(abi_align != .none);
+ if (overalign_big_int and field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits >= 128) {
+ return abi_align.maxStrict(.@"16");
}
- // extern
- if (field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits >= 128) {
- return ty_abi_align.maxStrict(.@"16");
- }
- return ty_abi_align;
-}
-
-pub fn unionFieldAlignmentSema(
- field_ty: Type,
- explicit_alignment: Alignment,
- layout: std.builtin.Type.ContainerLayout,
- pt: Zcu.PerThread,
-) SemaError!Alignment {
- return field_ty.unionFieldAlignmentInner(
- explicit_alignment,
- layout,
- .sema,
- pt.zcu,
- pt.tid,
- );
-}
-
-pub fn unionFieldAlignmentInner(
- field_ty: Type,
- explicit_alignment: Alignment,
- layout: std.builtin.Type.ContainerLayout,
- comptime strat: Type.ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) SemaError!Alignment {
- assert(layout != .@"packed");
- if (explicit_alignment != .none) return explicit_alignment;
- if (field_ty.isNoReturn(zcu)) return .none;
- return (try field_ty.abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar;
+ return abi_align;
}
-pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) Value {
+pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) ?Value {
const ip = &zcu.intern_pool;
switch (ip.indexToKey(ty.toIntern())) {
.struct_type => {
- const struct_type = ip.loadStructType(ty.toIntern());
- const val = struct_type.fieldInit(ip, index);
- // TODO: avoid using `unreachable` to indicate this.
- if (val == .none) return Value.@"unreachable";
- return Value.fromInterned(val);
+ const field_defaults = ip.loadStructType(ty.toIntern()).field_defaults.get(ip);
+ if (field_defaults.len == 0) return null;
+ if (field_defaults[index] == .none) return null;
+ return .fromInterned(field_defaults[index]);
},
.tuple_type => |tuple| {
const val = tuple.values.get(ip)[index];
- // TODO: avoid using `unreachable` to indicate this.
- if (val == .none) return Value.@"unreachable";
- return Value.fromInterned(val);
+ if (val == .none) return null;
+ return .fromInterned(val);
},
else => unreachable,
}
@@ -3324,9 +2412,9 @@ pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Val
switch (ip.indexToKey(ty.toIntern())) {
.struct_type => {
const struct_type = ip.loadStructType(ty.toIntern());
- if (struct_type.fieldIsComptime(ip, index)) {
- assert(struct_type.haveFieldInits(ip));
- return Value.fromInterned(struct_type.field_inits.get(ip)[index]);
+ if (struct_type.field_is_comptime_bits.get(ip, index)) {
+ assertHasInits(ty, zcu);
+ return .fromInterned(struct_type.field_defaults.get(ip)[index]);
} else {
return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(pt);
}
@@ -3336,7 +2424,7 @@ pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Val
if (val == .none) {
return Type.fromInterned(tuple.types.get(ip)[index]).onePossibleValue(pt);
} else {
- return Value.fromInterned(val);
+ return .fromInterned(val);
}
},
else => unreachable,
@@ -3346,7 +2434,7 @@ pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Val
pub fn structFieldIsComptime(ty: Type, index: usize, zcu: *const Zcu) bool {
const ip = &zcu.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
- .struct_type => ip.loadStructType(ty.toIntern()).fieldIsComptime(ip, index),
+ .struct_type => ip.loadStructType(ty.toIntern()).field_is_comptime_bits.get(ip, index),
.tuple_type => |tuple| tuple.values.get(ip)[index] != .none,
else => unreachable,
};
@@ -3357,15 +2445,15 @@ pub const FieldOffset = struct {
offset: u64,
};
-/// Supports structs and unions.
+/// Supports structs, tuples, and unions.
pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 {
+ assertHasLayout(ty, zcu);
const ip = &zcu.intern_pool;
switch (ip.indexToKey(ty.toIntern())) {
.struct_type => {
const struct_type = ip.loadStructType(ty.toIntern());
- assert(struct_type.haveLayout(ip));
assert(struct_type.layout != .@"packed");
- return struct_type.offsets.get(ip)[index];
+ return struct_type.field_offsets.get(ip)[index];
},
.tuple_type => |tuple| {
@@ -3391,7 +2479,7 @@ pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 {
.union_type => {
const union_type = ip.loadUnionType(ty.toIntern());
- if (!union_type.hasTag(ip))
+ if (union_type.runtime_tag == .none)
return 0;
const layout = Type.getUnionLayout(union_type, zcu);
if (layout.tag_align.compare(.gte, layout.payload_align)) {
@@ -3414,7 +2502,7 @@ pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Zcu.LazySrcLoc {
.struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) {
.declared => |d| d.zir_index,
.reified => |r| r.zir_index,
- .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index,
+ .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
},
else => return null,
},
@@ -3438,8 +2526,8 @@ pub fn isTuple(ty: Type, zcu: *const Zcu) bool {
};
}
-/// Traverses optional child types and error union payloads until the type
-/// is not a pointer. For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`.
+/// Traverses optional child types and error union payloads until the type is neither of those.
+/// For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`.
pub fn optEuBaseType(ty: Type, zcu: *const Zcu) Type {
var cur = ty;
while (true) switch (cur.zigTypeTag(zcu)) {
@@ -3488,7 +2576,7 @@ pub fn typeDeclInstAllowGeneratedTag(ty: Type, zcu: *const Zcu) ?InternPool.Trac
.union_type => ip.loadUnionType(ty.toIntern()).zir_index,
.enum_type => |e| switch (e) {
.declared, .reified => ip.loadEnumType(ty.toIntern()).zir_index.unwrap().?,
- .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index,
+ .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
},
.opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index,
else => null,
@@ -3505,7 +2593,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
.struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) {
.declared => |d| d.zir_index,
.reified => |r| r.zir_index,
- .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index,
+ .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
},
else => return null,
};
@@ -3520,10 +2608,10 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
.struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line,
.struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_line,
.extended => switch (inst.data.extended.opcode) {
- .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_line,
- .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_line,
- .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_line,
- .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_line,
+ .struct_decl => zir.getStructDecl(info.inst).src_line,
+ .union_decl => zir.getUnionDecl(info.inst).src_line,
+ .enum_decl => zir.getEnumDecl(info.inst).src_line,
+ .opaque_decl => zir.getOpaqueDecl(info.inst).src_line,
.reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.src_line,
.reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.src_line,
.reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.src_line,
@@ -3594,330 +2682,8 @@ pub fn packedStructFieldPtrInfo(
};
}
-pub fn resolveLayout(ty: Type, pt: Zcu.PerThread) SemaError!void {
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
- switch (ty.zigTypeTag(zcu)) {
- .@"struct" => switch (ip.indexToKey(ty.toIntern())) {
- .tuple_type => |tuple_type| for (0..tuple_type.types.len) |i| {
- const field_ty = Type.fromInterned(tuple_type.types.get(ip)[i]);
- try field_ty.resolveLayout(pt);
- },
- .struct_type => return ty.resolveStructInner(pt, .layout),
- else => unreachable,
- },
- .@"union" => return ty.resolveUnionInner(pt, .layout),
- .array => {
- if (ty.arrayLenIncludingSentinel(zcu) == 0) return;
- const elem_ty = ty.childType(zcu);
- return elem_ty.resolveLayout(pt);
- },
- .optional => {
- const payload_ty = ty.optionalChild(zcu);
- return payload_ty.resolveLayout(pt);
- },
- .error_union => {
- const payload_ty = ty.errorUnionPayload(zcu);
- return payload_ty.resolveLayout(pt);
- },
- .@"fn" => {
- const info = zcu.typeToFunc(ty).?;
- if (info.is_generic) {
- // Resolving of generic function types is deferred to when
- // the function is instantiated.
- return;
- }
- for (0..info.param_types.len) |i| {
- const param_ty = info.param_types.get(ip)[i];
- try Type.fromInterned(param_ty).resolveLayout(pt);
- }
- try Type.fromInterned(info.return_type).resolveLayout(pt);
- },
- else => {},
- }
-}
-
-pub fn resolveFields(ty: Type, pt: Zcu.PerThread) SemaError!void {
- const ip = &pt.zcu.intern_pool;
- const ty_ip = ty.toIntern();
-
- switch (ty_ip) {
- .none => unreachable,
-
- .u0_type,
- .i0_type,
- .u1_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u29_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u80_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_char_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f80_type,
- .f128_type,
- .anyopaque_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .adhoc_inferred_error_set_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .anyframe_type,
- .null_type,
- .undefined_type,
- .enum_literal_type,
- .ptr_usize_type,
- .ptr_const_comptime_int_type,
- .manyptr_u8_type,
- .manyptr_const_u8_type,
- .manyptr_const_u8_sentinel_0_type,
- .slice_const_u8_type,
- .slice_const_u8_sentinel_0_type,
- .optional_noreturn_type,
- .anyerror_void_error_union_type,
- .generic_poison_type,
- .empty_tuple_type,
- => {},
-
- .undef => unreachable,
- .zero => unreachable,
- .zero_usize => unreachable,
- .zero_u1 => unreachable,
- .zero_u8 => unreachable,
- .one => unreachable,
- .one_usize => unreachable,
- .one_u1 => unreachable,
- .one_u8 => unreachable,
- .four_u8 => unreachable,
- .negative_one => unreachable,
- .void_value => unreachable,
- .unreachable_value => unreachable,
- .null_value => unreachable,
- .bool_true => unreachable,
- .bool_false => unreachable,
- .empty_tuple => unreachable,
-
- else => switch (ty_ip.unwrap(ip).getTag(ip)) {
- .type_struct,
- .type_struct_packed,
- .type_struct_packed_inits,
- => return ty.resolveStructInner(pt, .fields),
-
- .type_union => return ty.resolveUnionInner(pt, .fields),
-
- else => {},
- },
- }
-}
-
-pub fn resolveFully(ty: Type, pt: Zcu.PerThread) SemaError!void {
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
-
- switch (ty.zigTypeTag(zcu)) {
- .type,
- .void,
- .bool,
- .noreturn,
- .int,
- .float,
- .comptime_float,
- .comptime_int,
- .undefined,
- .null,
- .error_set,
- .@"enum",
- .@"opaque",
- .frame,
- .@"anyframe",
- .vector,
- .enum_literal,
- => {},
-
- .pointer => return ty.childType(zcu).resolveFully(pt),
- .array => return ty.childType(zcu).resolveFully(pt),
- .optional => return ty.optionalChild(zcu).resolveFully(pt),
- .error_union => return ty.errorUnionPayload(zcu).resolveFully(pt),
- .@"fn" => {
- const info = zcu.typeToFunc(ty).?;
- if (info.is_generic) return;
- for (0..info.param_types.len) |i| {
- const param_ty = info.param_types.get(ip)[i];
- try Type.fromInterned(param_ty).resolveFully(pt);
- }
- try Type.fromInterned(info.return_type).resolveFully(pt);
- },
-
- .@"struct" => switch (ip.indexToKey(ty.toIntern())) {
- .tuple_type => |tuple_type| for (0..tuple_type.types.len) |i| {
- const field_ty = Type.fromInterned(tuple_type.types.get(ip)[i]);
- try field_ty.resolveFully(pt);
- },
- .struct_type => return ty.resolveStructInner(pt, .full),
- else => unreachable,
- },
- .@"union" => return ty.resolveUnionInner(pt, .full),
- }
-}
-
-pub fn resolveStructFieldInits(ty: Type, pt: Zcu.PerThread) SemaError!void {
- // TODO: stop calling this for tuples!
- _ = pt.zcu.typeToStruct(ty) orelse return;
- return ty.resolveStructInner(pt, .inits);
-}
-
-pub fn resolveStructAlignment(ty: Type, pt: Zcu.PerThread) SemaError!void {
- return ty.resolveStructInner(pt, .alignment);
-}
-
-pub fn resolveUnionAlignment(ty: Type, pt: Zcu.PerThread) SemaError!void {
- return ty.resolveUnionInner(pt, .alignment);
-}
-
-/// `ty` must be a struct.
-fn resolveStructInner(
- ty: Type,
- pt: Zcu.PerThread,
- resolution: enum { fields, inits, alignment, layout, full },
-) SemaError!void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- const struct_obj = zcu.typeToStruct(ty).?;
- const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
-
- if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
- return error.AnalysisFail;
- }
-
- if (zcu.comp.debugIncremental()) {
- const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner);
- info.last_update_gen = zcu.generation;
- }
-
- var analysis_arena = std.heap.ArenaAllocator.init(gpa);
- defer analysis_arena.deinit();
-
- var comptime_err_ret_trace = std.array_list.Managed(Zcu.LazySrcLoc).init(gpa);
- defer comptime_err_ret_trace.deinit();
-
- const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir.?;
- var sema: Sema = .{
- .pt = pt,
- .gpa = gpa,
- .arena = analysis_arena.allocator(),
- .code = zir,
- .owner = owner,
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = Type.void,
- .fn_ret_ty_ies = null,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
-
- (switch (resolution) {
- .fields => sema.resolveStructFieldTypes(ty.toIntern(), struct_obj),
- .inits => sema.resolveStructFieldInits(ty),
- .alignment => sema.resolveStructAlignment(ty.toIntern(), struct_obj),
- .layout => sema.resolveStructLayout(ty),
- .full => sema.resolveStructFully(ty),
- }) catch |err| switch (err) {
- error.AnalysisFail => {
- if (!zcu.failed_analysis.contains(owner)) {
- try zcu.transitive_failed_analysis.put(gpa, owner, {});
- }
- return error.AnalysisFail;
- },
- error.OutOfMemory, error.Canceled => |e| return e,
- };
-}
-
-/// `ty` must be a union.
-fn resolveUnionInner(
- ty: Type,
- pt: Zcu.PerThread,
- resolution: enum { fields, alignment, layout, full },
-) SemaError!void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- const union_obj = zcu.typeToUnion(ty).?;
- const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
-
- if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
- return error.AnalysisFail;
- }
-
- if (zcu.comp.debugIncremental()) {
- const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner);
- info.last_update_gen = zcu.generation;
- }
-
- var analysis_arena = std.heap.ArenaAllocator.init(gpa);
- defer analysis_arena.deinit();
-
- var comptime_err_ret_trace = std.array_list.Managed(Zcu.LazySrcLoc).init(gpa);
- defer comptime_err_ret_trace.deinit();
-
- const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir.?;
- var sema: Sema = .{
- .pt = pt,
- .gpa = gpa,
- .arena = analysis_arena.allocator(),
- .code = zir,
- .owner = owner,
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = Type.void,
- .fn_ret_ty_ies = null,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
-
- (switch (resolution) {
- .fields => sema.resolveUnionFieldTypes(ty, union_obj),
- .alignment => sema.resolveUnionAlignment(ty, union_obj),
- .layout => sema.resolveUnionLayout(ty),
- .full => sema.resolveUnionFully(ty),
- }) catch |err| switch (err) {
- error.AnalysisFail => {
- if (!zcu.failed_analysis.contains(owner)) {
- try zcu.transitive_failed_analysis.put(gpa, owner, {});
- }
- return error.AnalysisFail;
- },
- error.OutOfMemory => |e| return e,
- error.Canceled => |e| return e,
- };
-}
-
pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
const ip = &zcu.intern_pool;
- assert(loaded_union.haveLayout(ip));
var most_aligned_field: u32 = 0;
var most_aligned_field_align: InternPool.Alignment = .@"1";
var most_aligned_field_size: u64 = 0;
@@ -3928,11 +2694,14 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
const field_ty: Type = .fromInterned(field_ty_ip_index);
if (field_ty.isNoReturn(zcu)) continue;
- const explicit_align = loaded_union.fieldAlign(ip, field_index);
- const field_align = if (explicit_align != .none)
- explicit_align
- else
- field_ty.abiAlignment(zcu);
+ const field_align: InternPool.Alignment = a: {
+ const explicit_aligns = loaded_union.field_aligns.get(ip);
+ if (explicit_aligns.len > 0) {
+ const a = explicit_aligns[field_index];
+ if (a != .none) break :a a;
+ }
+ break :a field_ty.abiAlignment(zcu);
+ };
if (field_ty.hasRuntimeBits(zcu)) {
const field_size = field_ty.abiSize(zcu);
if (field_size > payload_size) {
@@ -3947,8 +2716,9 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
}
payload_align = payload_align.max(field_align);
}
- const have_tag = loaded_union.flagsUnordered(ip).runtime_tag.hasTag();
- if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(zcu)) {
+ if (loaded_union.runtime_tag == .none or
+ !Type.fromInterned(loaded_union.enum_tag_type).hasRuntimeBits(zcu))
+ {
return .{
.abi_size = payload_align.forward(payload_size),
.abi_align = payload_align,
@@ -3963,10 +2733,10 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
};
}
- const tag_size = Type.fromInterned(loaded_union.enum_tag_ty).abiSize(zcu);
- const tag_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(zcu).max(.@"1");
+ const tag_size = Type.fromInterned(loaded_union.enum_tag_type).abiSize(zcu);
+ const tag_align = Type.fromInterned(loaded_union.enum_tag_type).abiAlignment(zcu).max(.@"1");
return .{
- .abi_size = loaded_union.sizeUnordered(ip),
+ .abi_size = loaded_union.size,
.abi_align = tag_align.max(payload_align),
.most_aligned_field = most_aligned_field,
.most_aligned_field_size = most_aligned_field_size,
@@ -3975,7 +2745,7 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
.payload_align = payload_align,
.tag_align = tag_align,
.tag_size = tag_size,
- .padding = loaded_union.paddingUnordered(ip),
+ .padding = loaded_union.padding,
};
}
@@ -3989,10 +2759,17 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
/// 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 {
const zcu = pt.zcu;
const ptr_info = ptr_ty.ptrInfo(zcu);
- const elem_ty = ptr_ty.elemType2(zcu);
+ 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);
@@ -4024,7 +2801,7 @@ pub fn elemPtrType(ptr_ty: Type, offset: ?usize, pt: Zcu.PerThread) !Type {
}
// 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 = (try elem_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar;
+ 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
@@ -4037,7 +2814,7 @@ pub fn elemPtrType(ptr_ty: Type, offset: ?usize, pt: Zcu.PerThread) !Type {
assert(new_align != .none);
break :a new_align;
};
- return pt.ptrTypeSema(.{
+ return pt.ptrType(.{
.child = elem_ty.toIntern(),
.flags = .{
.alignment = alignment,
@@ -4069,11 +2846,107 @@ pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTermina
/// Returns `null` otherwise.
pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool {
if (ty.zigTypeTag(zcu) != .optional and !ty.isCPtr(zcu)) return false;
- const child = ty.optionalChild(zcu);
- if (child.zigTypeTag(zcu) == .noreturn) return true; // `?noreturn` is always null
+ if (ty.optionalChild(zcu).isNoReturn(zcu)) return true; // `?noreturn` is always null
return null;
}
+/// Returns true if `ty` is allowed in packed types.
+pub fn packable(ty: Type, zcu: *const Zcu) bool {
+ return switch (ty.zigTypeTag(zcu)) {
+ .type,
+ .comptime_float,
+ .comptime_int,
+ .enum_literal,
+ .undefined,
+ .null,
+ .error_union,
+ .error_set,
+ .frame,
+ .noreturn,
+ .@"opaque",
+ .@"anyframe",
+ .@"fn",
+ .array,
+ => false,
+ .optional => return ty.isPtrLikeOptional(zcu),
+ .void,
+ .bool,
+ .float,
+ .int,
+ .vector,
+ => true,
+ .@"enum" => zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_is_explicit,
+ .pointer => !ty.isSlice(zcu),
+ .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
+ };
+}
+
+/// Asserts that `ty` has resolved layout.
+pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
+ switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
+ .int_type,
+ .ptr_type,
+ .anyframe_type,
+ .simple_type,
+ .opaque_type,
+ .enum_type,
+ .error_set_type,
+ .inferred_error_set_type,
+ => {},
+ .func_type => |func_type| {
+ for (func_type.param_types.get(&zcu.intern_pool)) |param_ty| {
+ assertHasLayout(.fromInterned(param_ty), zcu);
+ }
+ assertHasLayout(.fromInterned(func_type.return_type), zcu);
+ },
+ .array_type => |arr| assertHasLayout(.fromInterned(arr.child), zcu),
+ .vector_type => |vec| assertHasLayout(.fromInterned(vec.child), zcu),
+ .opt_type => |child| assertHasLayout(.fromInterned(child), zcu),
+ .error_union_type => |eu| assertHasLayout(.fromInterned(eu.payload_type), zcu),
+ .tuple_type => |tuple| for (tuple.types.get(&zcu.intern_pool)) |field_ty| {
+ assertHasLayout(.fromInterned(field_ty), zcu);
+ },
+ .struct_type, .union_type => {
+ const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });
+ assert(!zcu.outdated.contains(unit));
+ assert(!zcu.potentially_outdated.contains(unit));
+ },
+ else => unreachable, // assertion failure; not a struct or union
+
+ // values, not types
+ .simple_value,
+ .variable,
+ .@"extern",
+ .func,
+ .int,
+ .err,
+ .error_union,
+ .enum_literal,
+ .enum_tag,
+ .empty_enum_value,
+ .float,
+ .ptr,
+ .slice,
+ .opt,
+ .aggregate,
+ .un,
+ // memoization, not types
+ .memoized_call,
+ => unreachable,
+ }
+}
+
+/// Asserts that `ty` is an enum or struct type whose field values/defaults are resolved.
+pub fn assertHasInits(ty: Type, zcu: *const Zcu) void {
+ switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
+ .struct_type, .enum_type => {},
+ else => unreachable,
+ }
+ const unit: InternPool.AnalUnit = .wrap(.{ .type_inits = ty.toIntern() });
+ assert(!zcu.outdated.contains(unit));
+ assert(!zcu.potentially_outdated.contains(unit));
+}
+
/// Recursively walks the type and marks for each subtype how many times it has been seen
fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUnmanaged(Type, u16)) error{OutOfMemory}!void {
const zcu = pt.zcu;
diff --git a/src/Value.zig b/src/Value.zig
index 103140d3c9a3df0878f1ceb254ba2bb483ea2865..5986eee6d652b135fd2b28bfc2f839bdd578486e 100644
--- a/src/Value.zig
+++ b/src/Value.zig
@@ -146,80 +146,22 @@ pub fn toType(self: Value) Type {
return Type.fromInterned(self.toIntern());
}
-pub fn intFromEnum(val: Value, ty: Type, pt: Zcu.PerThread) Allocator.Error!Value {
- const ip = &pt.zcu.intern_pool;
- const enum_ty = ip.typeOf(val.toIntern());
- return switch (ip.indexToKey(enum_ty)) {
- // Assume it is already an integer and return it directly.
- .simple_type, .int_type => val,
- .enum_literal => |enum_literal| {
- const field_index = ty.enumFieldIndex(enum_literal, pt.zcu).?;
- switch (ip.indexToKey(ty.toIntern())) {
- // Assume it is already an integer and return it directly.
- .simple_type, .int_type => return val,
- .enum_type => {
- const enum_type = ip.loadEnumType(ty.toIntern());
- if (enum_type.values.len != 0) {
- return Value.fromInterned(enum_type.values.get(ip)[field_index]);
- } else {
- // Field index and integer values are the same.
- return pt.intValue(Type.fromInterned(enum_type.tag_ty), field_index);
- }
- },
- else => unreachable,
- }
- },
- .enum_type => try pt.getCoerced(val, Type.fromInterned(ip.loadEnumType(enum_ty).tag_ty)),
- else => unreachable,
- };
+pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {
+ return .fromInterned(zcu.intern_pool.indexToKey(val.toIntern()).enum_tag.int);
}
-pub const ResolveStrat = Type.ResolveStrat;
-
-/// Asserts the value is an integer.
+/// Asserts that `val` is an integer.
pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst {
- return val.toBigIntAdvanced(space, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn toBigIntSema(val: Value, space: *BigIntSpace, pt: Zcu.PerThread) !BigIntConst {
- return try val.toBigIntAdvanced(space, .sema, pt.zcu, pt.tid);
-}
-
-/// Asserts the value is an integer.
-pub fn toBigIntAdvanced(
- val: Value,
- space: *BigIntSpace,
- comptime strat: ResolveStrat,
- zcu: *Zcu,
- tid: strat.Tid(),
-) Zcu.SemaError!BigIntConst {
+ if (val.getUnsignedInt(zcu)) |x| {
+ return BigIntMutable.init(&space.limbs, x).toConst();
+ }
const ip = &zcu.intern_pool;
- return switch (val.toIntern()) {
- .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
- .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
- .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
- else => switch (ip.indexToKey(val.toIntern())) {
- .int => |int| switch (int.storage) {
- .u64, .i64, .big_int => int.storage.toBigInt(space),
- .lazy_align, .lazy_size => |ty| {
- if (strat == .sema) try Type.fromInterned(ty).resolveLayout(strat.pt(zcu, tid));
- const x = switch (int.storage) {
- else => unreachable,
- .lazy_align => Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0,
- .lazy_size => Type.fromInterned(ty).abiSize(zcu),
- };
- return BigIntMutable.init(&space.limbs, x).toConst();
- },
- },
- .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, strat, zcu, tid),
- .opt, .ptr => BigIntMutable.init(
- &space.limbs,
- (try val.getUnsignedIntInner(strat, zcu, tid)).?,
- ).toConst(),
- .err => |err| BigIntMutable.init(&space.limbs, ip.getErrorValueIfExists(err.name).?).toConst(),
- else => unreachable,
- },
+ const int_key = switch (ip.indexToKey(val.toIntern())) {
+ .enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int,
+ .int => |int| int,
+ else => unreachable,
};
+ return int_key.storage.toBigInt(space);
}
pub fn isFuncBody(val: Value, zcu: *Zcu) bool {
@@ -240,31 +182,17 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable {
};
}
-/// If the value fits in a u64, return it, otherwise null.
-/// Asserts not undefined.
-pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
- return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable;
-}
-
-/// Asserts the value is an integer and it fits in a u64
+/// Asserts the value is a (defined) integer and it fits in a u64.
pub fn toUnsignedInt(val: Value, zcu: *const Zcu) u64 {
return getUnsignedInt(val, zcu).?;
}
-pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 {
- return try val.getUnsignedIntInner(.sema, pt.zcu, pt.tid);
-}
-
/// If the value fits in a u64, return it, otherwise null.
/// Asserts not undefined.
-pub fn getUnsignedIntInner(
- val: Value,
- comptime strat: ResolveStrat,
- zcu: strat.ZcuPtr(),
- tid: strat.Tid(),
-) !?u64 {
+pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
return switch (val.toIntern()) {
.undef => unreachable,
+ .null_value => 0,
.bool_false => 0,
.bool_true => 1,
else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
@@ -273,37 +201,27 @@ pub fn getUnsignedIntInner(
.big_int => |big_int| big_int.toInt(u64) catch null,
.u64 => |x| x,
.i64 => |x| std.math.cast(u64, x),
- .lazy_align => |ty| (try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar.toByteUnits() orelse 0,
- .lazy_size => |ty| (try Type.fromInterned(ty).abiSizeInner(strat.toLazy(), zcu, tid)).scalar,
},
.ptr => |ptr| switch (ptr.base_addr) {
.int => ptr.byte_offset,
.field => |field| {
- const base_addr = (try Value.fromInterned(field.base).getUnsignedIntInner(strat, zcu, tid)) orelse return null;
+ const base_addr = Value.fromInterned(field.base).getUnsignedInt(zcu) orelse return null;
const struct_ty = Value.fromInterned(field.base).typeOf(zcu).childType(zcu);
- if (strat == .sema) {
- const pt = strat.pt(zcu, tid);
- try struct_ty.resolveLayout(pt);
- }
return base_addr + struct_ty.structFieldOffset(@intCast(field.index), zcu) + ptr.byte_offset;
},
else => null,
},
.opt => |opt| switch (opt.val) {
.none => 0,
- else => |payload| Value.fromInterned(payload).getUnsignedIntInner(strat, zcu, tid),
+ else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu),
},
- .enum_tag => |enum_tag| return Value.fromInterned(enum_tag.int).getUnsignedIntInner(strat, zcu, tid),
+ .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu),
+ .err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?,
else => null,
},
};
}
-/// Asserts the value is an integer and it fits in a u64
-pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 {
- return (try getUnsignedIntInner(val, .sema, pt.zcu, pt.tid)).?;
-}
-
/// Asserts the value is an integer and it fits in a i64
pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 {
return switch (val.toIntern()) {
@@ -314,8 +232,6 @@ pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 {
.big_int => |big_int| big_int.toInt(i64) catch unreachable,
.i64 => |x| x,
.u64 => |x| @intCast(x),
- .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0),
- .lazy_size => |ty| @intCast(Type.fromInterned(ty).abiSize(zcu)),
},
else => unreachable,
},
@@ -487,22 +403,16 @@ pub fn writeToPackedMemory(
buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
}
},
- .int, .@"enum" => {
- if (buffer.len == 0) return;
+ .@"enum" => {
+ const int_val = val.intFromEnum(zcu);
+ return int_val.writeToPackedMemory(int_val.typeOf(zcu), pt, buffer, bit_offset);
+ },
+ .int => {
const bits = ty.intInfo(zcu).bits;
- if (bits == 0) return;
-
- switch (ip.indexToKey((try val.intFromEnum(ty, pt)).toIntern()).int.storage) {
+ if (bits == 0 or buffer.len == 0) return;
+ switch (ip.indexToKey(val.toIntern()).int.storage) {
inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
.big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
- .lazy_align => |lazy_align| {
- const num = Type.fromInterned(lazy_align).abiAlignment(zcu).toByteUnits() orelse 0;
- std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
- },
- .lazy_size => |lazy_size| {
- const num = Type.fromInterned(lazy_size).abiSize(zcu);
- std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
- },
}
},
.float => switch (ty.floatBits(target)) {
@@ -548,19 +458,15 @@ pub fn writeToPackedMemory(
},
.@"union" => {
const union_obj = zcu.typeToUnion(ty).?;
- switch (union_obj.flagsUnordered(ip).layout) {
- .auto, .@"extern" => unreachable, // Handled in non-packed writeToMemory
- .@"packed" => {
- if (val.unionTag(zcu)) |union_tag| {
- const field_index = zcu.unionTagFieldIndex(union_obj, union_tag).?;
- const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
- const field_val = try val.fieldValue(pt, field_index);
- return field_val.writeToPackedMemory(field_type, pt, buffer, bit_offset);
- } else {
- const backing_ty = try ty.unionBackingType(pt);
- return val.unionValue(zcu).writeToPackedMemory(backing_ty, pt, buffer, bit_offset);
- }
- },
+ assert(union_obj.layout == .@"packed");
+ if (val.unionTag(zcu)) |union_tag| {
+ const field_index = zcu.unionTagFieldIndex(union_obj, union_tag).?;
+ const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
+ const field_val = try val.fieldValue(pt, field_index);
+ return field_val.writeToPackedMemory(field_type, pt, buffer, bit_offset);
+ } else {
+ const backing_ty = try ty.unionBackingType(pt);
+ return val.unionValue(zcu).writeToPackedMemory(backing_ty, pt, buffer, bit_offset);
}
},
.pointer => {
@@ -729,24 +635,15 @@ pub fn readFromPackedMemory(
},
.pointer => {
assert(!ty.isSlice(zcu)); // No well defined layout.
- const int_val = try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena);
- return Value.fromInterned(try pt.intern(.{ .ptr = .{
- .ty = ty.toIntern(),
- .base_addr = .int,
- .byte_offset = int_val.toUnsignedInt(zcu),
- } }));
+ const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
+ return pt.ptrIntValue(ty, addr);
},
.optional => {
assert(ty.isPtrLikeOptional(zcu));
- const child_ty = ty.optionalChild(zcu);
- const child_val = try readFromPackedMemory(child_ty, pt, buffer, bit_offset, arena);
+ const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
return Value.fromInterned(try pt.intern(.{ .opt = .{
.ty = ty.toIntern(),
- .val = switch (child_val.orderAgainstZero(zcu)) {
- .lt => unreachable,
- .eq => .none,
- .gt => child_val.toIntern(),
- },
+ .val = (try pt.ptrIntValue(ty.childType(zcu), addr)).toIntern(),
} }));
},
else => @panic("TODO implement readFromPackedMemory for more types"),
@@ -764,8 +661,6 @@ pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T {
}
return @floatFromInt(x);
},
- .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0),
- .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(zcu)),
},
.float => |float| switch (float.storage) {
inline else => |x| @floatCast(x),
@@ -819,110 +714,8 @@ pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value {
} }));
}
-pub fn orderAgainstZero(lhs: Value, zcu: *Zcu) std.math.Order {
- return orderAgainstZeroInner(lhs, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn orderAgainstZeroSema(lhs: Value, pt: Zcu.PerThread) !std.math.Order {
- return try orderAgainstZeroInner(lhs, .sema, pt.zcu, pt.tid);
-}
-
-pub fn orderAgainstZeroInner(
- lhs: Value,
- comptime strat: ResolveStrat,
- zcu: *Zcu,
- tid: strat.Tid(),
-) Zcu.SemaError!std.math.Order {
- return switch (lhs.toIntern()) {
- .bool_false => .eq,
- .bool_true => .gt,
- else => switch (zcu.intern_pool.indexToKey(lhs.toIntern())) {
- .ptr => |ptr| if (ptr.byte_offset > 0) .gt else switch (ptr.base_addr) {
- .nav, .comptime_alloc, .comptime_field => .gt,
- .int => .eq,
- else => unreachable,
- },
- .int => |int| switch (int.storage) {
- .big_int => |big_int| big_int.orderAgainstScalar(0),
- inline .u64, .i64 => |x| std.math.order(x, 0),
- .lazy_align => .gt, // alignment is never 0
- .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsInner(
- false,
- strat.toLazy(),
- zcu,
- tid,
- ) catch |err| switch (err) {
- error.NeedLazy => unreachable,
- else => |e| return e,
- }) .gt else .eq,
- },
- .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroInner(strat, zcu, tid),
- .float => |float| switch (float.storage) {
- inline else => |x| std.math.order(x, 0),
- },
- .err => .gt, // error values cannot be 0
- else => unreachable,
- },
- };
-}
-
-/// Asserts the value is comparable.
-pub fn order(lhs: Value, rhs: Value, zcu: *Zcu) std.math.Order {
- return orderAdvanced(lhs, rhs, .normal, zcu, {}) catch unreachable;
-}
-
-/// Asserts the value is comparable.
-pub fn orderAdvanced(
- lhs: Value,
- rhs: Value,
- comptime strat: ResolveStrat,
- zcu: *Zcu,
- tid: strat.Tid(),
-) !std.math.Order {
- const lhs_against_zero = try lhs.orderAgainstZeroInner(strat, zcu, tid);
- const rhs_against_zero = try rhs.orderAgainstZeroInner(strat, zcu, tid);
- switch (lhs_against_zero) {
- .lt => if (rhs_against_zero != .lt) return .lt,
- .eq => return rhs_against_zero.invert(),
- .gt => {},
- }
- switch (rhs_against_zero) {
- .lt => if (lhs_against_zero != .lt) return .gt,
- .eq => return lhs_against_zero,
- .gt => {},
- }
-
- if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {
- const lhs_f128 = lhs.toFloat(f128, zcu);
- const rhs_f128 = rhs.toFloat(f128, zcu);
- return std.math.order(lhs_f128, rhs_f128);
- }
-
- var lhs_bigint_space: BigIntSpace = undefined;
- var rhs_bigint_space: BigIntSpace = undefined;
- const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, strat, zcu, tid);
- const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, strat, zcu, tid);
- return lhs_bigint.order(rhs_bigint);
-}
-
-/// Asserts the value is comparable. Does not take a type parameter because it supports
-/// comparisons between heterogeneous types.
+/// Asserts the value is comparable. Supports comparisons between heterogeneous types.
pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *Zcu) bool {
- return compareHeteroAdvanced(lhs, op, rhs, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn compareHeteroSema(lhs: Value, op: std.math.CompareOperator, rhs: Value, pt: Zcu.PerThread) !bool {
- return try compareHeteroAdvanced(lhs, op, rhs, .sema, pt.zcu, pt.tid);
-}
-
-pub fn compareHeteroAdvanced(
- lhs: Value,
- op: std.math.CompareOperator,
- rhs: Value,
- comptime strat: ResolveStrat,
- zcu: *Zcu,
- tid: strat.Tid(),
-) !bool {
if (lhs.pointerNav(zcu)) |lhs_nav| {
if (rhs.pointerNav(zcu)) |rhs_nav| {
switch (op) {
@@ -944,9 +737,21 @@ pub fn compareHeteroAdvanced(
else => {},
}
}
-
if (lhs.isNan(zcu) or rhs.isNan(zcu)) return op == .neq;
- return (try orderAdvanced(lhs, rhs, strat, zcu, tid)).compare(op);
+ return order(lhs, rhs, zcu).compare(op);
+}
+
+pub fn order(lhs: Value, rhs: Value, zcu: *Zcu) std.math.Order {
+ if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {
+ const lhs_f128 = lhs.toFloat(f128, zcu);
+ const rhs_f128 = rhs.toFloat(f128, zcu);
+ return std.math.order(lhs_f128, rhs_f128);
+ }
+ var lhs_bigint_space: BigIntSpace = undefined;
+ var rhs_bigint_space: BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, zcu);
+ const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu);
+ return lhs_bigint.order(rhs_bigint);
}
/// Asserts the values are comparable. Both operands have type `ty`.
@@ -987,56 +792,32 @@ pub fn compareScalar(
/// Returns `false` if the value or any vector element is undefined.
///
/// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
+/// TODO MLUGG: lowkey wanna delete this
pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, zcu: *Zcu) bool {
- return compareAllWithZeroAdvancedExtra(lhs, op, .normal, zcu, {}) catch unreachable;
-}
-
-pub fn compareAllWithZeroSema(
- lhs: Value,
- op: std.math.CompareOperator,
- pt: Zcu.PerThread,
-) Zcu.CompileError!bool {
- return compareAllWithZeroAdvancedExtra(lhs, op, .sema, pt.zcu, pt.tid);
-}
-
-pub fn compareAllWithZeroAdvancedExtra(
- lhs: Value,
- op: std.math.CompareOperator,
- comptime strat: ResolveStrat,
- zcu: *Zcu,
- tid: strat.Tid(),
-) Zcu.CompileError!bool {
- if (lhs.isInf(zcu)) {
- switch (op) {
- .neq => return true,
- .eq => return false,
- .gt, .gte => return !lhs.isNegativeInf(zcu),
- .lt, .lte => return lhs.isNegativeInf(zcu),
- }
- }
-
- switch (zcu.intern_pool.indexToKey(lhs.toIntern())) {
+ return switch (zcu.intern_pool.indexToKey(lhs.toIntern())) {
.float => |float| switch (float.storage) {
- inline else => |x| if (std.math.isNan(x)) return op == .neq,
+ inline else => |x| std.math.compare(x, op, 0),
},
- .aggregate => |aggregate| return switch (aggregate.storage) {
- .bytes => |bytes| for (bytes.toSlice(lhs.typeOf(zcu).arrayLenIncludingSentinel(zcu), &zcu.intern_pool)) |byte| {
- if (!std.math.order(byte, 0).compare(op)) break false;
+ .aggregate => |aggregate| switch (aggregate.storage) {
+ .bytes => |bytes| for (bytes.toSlice(
+ lhs.typeOf(zcu).arrayLenIncludingSentinel(zcu),
+ &zcu.intern_pool,
+ )) |byte| {
+ if (!std.math.compare(byte, op, 0)) break false;
} else true,
.elems => |elems| for (elems) |elem| {
- if (!try Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, strat, zcu, tid)) break false;
+ if (!Value.fromInterned(elem).compareAllWithZero(op, zcu)) break false;
} else true,
- .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, strat, zcu, tid),
+ .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZero(op, zcu),
},
- .undef => return false,
- else => {},
- }
- return (try orderAgainstZeroInner(lhs, strat, zcu, tid)).compare(op);
+ .undef => false,
+ else => order(lhs, .zero_comptime_int, zcu).compare(op),
+ };
}
pub fn eql(a: Value, b: Value, ty: Type, zcu: *Zcu) bool {
- assert(zcu.intern_pool.typeOf(a.toIntern()) == ty.toIntern());
- assert(zcu.intern_pool.typeOf(b.toIntern()) == ty.toIntern());
+ assert(a.typeOf(zcu).toIntern() == ty.toIntern());
+ assert(b.typeOf(zcu).toIntern() == ty.toIntern());
return a.toIntern() == b.toIntern();
}
@@ -1088,16 +869,13 @@ pub fn pointerNav(val: Value, zcu: *Zcu) ?InternPool.Nav.Index {
pub const slice_ptr_index = 0;
pub const slice_len_index = 1;
+pub fn sliceLen(val: Value, zcu: *Zcu) u64 {
+ return Value.fromInterned(zcu.intern_pool.sliceLen(val.toIntern())).toUnsignedInt(zcu);
+}
pub fn slicePtr(val: Value, zcu: *Zcu) Value {
return Value.fromInterned(zcu.intern_pool.slicePtr(val.toIntern()));
}
-/// Gets the `len` field of a slice value as a `u64`.
-/// Resolves the length using `Sema` if necessary.
-pub fn sliceLen(val: Value, pt: Zcu.PerThread) !u64 {
- return Value.fromInterned(pt.zcu.intern_pool.sliceLen(val.toIntern())).toUnsignedIntSema(pt);
-}
-
/// Asserts the value is an aggregate, and returns the element value at the given index.
pub fn elemValue(val: Value, pt: Zcu.PerThread, index: usize) Allocator.Error!Value {
const zcu = pt.zcu;
@@ -1123,62 +901,6 @@ pub fn elemValue(val: Value, pt: Zcu.PerThread, index: usize) Allocator.Error!Va
}
}
-pub fn isLazyAlign(val: Value, zcu: *Zcu) bool {
- return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
- .int => |int| int.storage == .lazy_align,
- else => false,
- };
-}
-
-pub fn isLazySize(val: Value, zcu: *Zcu) bool {
- return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
- .int => |int| int.storage == .lazy_size,
- else => false,
- };
-}
-
-// Asserts that the provided start/end are in-bounds.
-pub fn sliceArray(
- val: Value,
- sema: *Sema,
- start: usize,
- end: usize,
-) error{OutOfMemory}!Value {
- const pt = sema.pt;
- const ip = &pt.zcu.intern_pool;
- const io = pt.zcu.comp.io;
- return Value.fromInterned(try pt.intern(.{
- .aggregate = .{
- .ty = switch (pt.zcu.intern_pool.indexToKey(pt.zcu.intern_pool.typeOf(val.toIntern()))) {
- .array_type => |array_type| try pt.arrayType(.{
- .len = @intCast(end - start),
- .child = array_type.child,
- .sentinel = if (end == array_type.len) array_type.sentinel else .none,
- }),
- .vector_type => |vector_type| try pt.vectorType(.{
- .len = @intCast(end - start),
- .child = vector_type.child,
- }),
- else => unreachable,
- }.toIntern(),
- .storage = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
- .bytes => |bytes| storage: {
- try ip.string_bytes.ensureUnusedCapacity(sema.gpa, end - start + 1);
- break :storage .{ .bytes = try ip.getOrPutString(
- sema.gpa,
- io,
- bytes.toSlice(end, ip)[start..],
- .maybe_embedded_nulls,
- ) };
- },
- // TODO: write something like getCoercedInts to avoid needing to dupe
- .elems => |elems| .{ .elems = try sema.arena.dupe(InternPool.Index, elems[start..end]) },
- .repeated_elem => |elem| .{ .repeated_elem = elem },
- },
- },
- }));
-}
-
pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value {
const zcu = pt.zcu;
return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
@@ -1334,63 +1056,6 @@ pub fn isFloat(self: Value, zcu: *const Zcu) bool {
};
}
-pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, zcu: *Zcu) !Value {
- return floatFromIntAdvanced(val, arena, int_ty, float_ty, zcu, .normal) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => unreachable,
- };
-}
-
-pub fn floatFromIntAdvanced(
- val: Value,
- arena: Allocator,
- int_ty: Type,
- float_ty: Type,
- pt: Zcu.PerThread,
- comptime strat: ResolveStrat,
-) !Value {
- const zcu = pt.zcu;
- if (int_ty.zigTypeTag(zcu) == .vector) {
- const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(zcu));
- const scalar_ty = float_ty.scalarType(zcu);
- for (result_data, 0..) |*scalar, i| {
- const elem_val = try val.elemValue(pt, i);
- scalar.* = (try floatFromIntScalar(elem_val, scalar_ty, pt, strat)).toIntern();
- }
- return pt.aggregateValue(float_ty, result_data);
- }
- return floatFromIntScalar(val, float_ty, pt, strat);
-}
-
-pub fn floatFromIntScalar(val: Value, float_ty: Type, pt: Zcu.PerThread, comptime strat: ResolveStrat) !Value {
- return switch (pt.zcu.intern_pool.indexToKey(val.toIntern())) {
- .undef => try pt.undefValue(float_ty),
- .int => |int| switch (int.storage) {
- .big_int => |big_int| pt.floatValue(float_ty, big_int.toFloat(f128, .nearest_even)[0]),
- inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, pt),
- .lazy_align => |ty| floatFromIntInner((try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), pt.zcu, pt.tid)).scalar.toByteUnits() orelse 0, float_ty, pt),
- .lazy_size => |ty| floatFromIntInner((try Type.fromInterned(ty).abiSizeInner(strat.toLazy(), pt.zcu, pt.tid)).scalar, float_ty, pt),
- },
- else => unreachable,
- };
-}
-
-fn floatFromIntInner(x: anytype, dest_ty: Type, pt: Zcu.PerThread) !Value {
- const target = pt.zcu.getTarget();
- const storage: InternPool.Key.Float.Storage = switch (dest_ty.floatBits(target)) {
- 16 => .{ .f16 = @floatFromInt(x) },
- 32 => .{ .f32 = @floatFromInt(x) },
- 64 => .{ .f64 = @floatFromInt(x) },
- 80 => .{ .f80 = @floatFromInt(x) },
- 128 => .{ .f128 = @floatFromInt(x) },
- else => unreachable,
- };
- return Value.fromInterned(try pt.intern(.{ .float = .{
- .ty = dest_ty.toIntern(),
- .storage = storage,
- } }));
-}
-
fn calcLimbLenFloat(scalar: anytype) usize {
if (scalar == 0) {
return 1;
@@ -1410,11 +1075,11 @@ pub fn numberMax(lhs: Value, rhs: Value, zcu: *Zcu) Value {
if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef;
if (lhs.isNan(zcu)) return rhs;
if (rhs.isNan(zcu)) return lhs;
-
- return switch (order(lhs, rhs, zcu)) {
- .lt => rhs,
- .gt, .eq => lhs,
- };
+ if (compareHetero(lhs, .gt, rhs, zcu)) {
+ return lhs;
+ } else {
+ return rhs;
+ }
}
/// Supports both floats and ints; handles undefined.
@@ -1422,11 +1087,11 @@ pub fn numberMin(lhs: Value, rhs: Value, zcu: *Zcu) Value {
if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef;
if (lhs.isNan(zcu)) return rhs;
if (rhs.isNan(zcu)) return lhs;
-
- return switch (order(lhs, rhs, zcu)) {
- .lt => lhs,
- .gt, .eq => rhs,
- };
+ if (compareHetero(lhs, .lt, rhs, zcu)) {
+ return lhs;
+ } else {
+ return rhs;
+ }
}
/// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
@@ -2035,6 +1700,7 @@ pub fn makeBool(x: bool) Value {
/// 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);
@@ -2044,7 +1710,7 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
assert(ptr_size == .one or ptr_size == .c);
assert(opt_ty.zigTypeTag(zcu) == .optional);
- const result_ty = try pt.ptrTypeSema(info: {
+ const result_ty = try pt.ptrType(info: {
var new = parent_ptr_ty.ptrInfo(zcu);
// We can correctly preserve alignment `.none`, since an optional has the same
// natural alignment as its child type.
@@ -2070,6 +1736,7 @@ 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);
@@ -2078,7 +1745,7 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
assert(parent_ptr_ty.ptrSize(zcu) == .one);
assert(eu_ty.zigTypeTag(zcu) == .error_union);
- const result_ty = try pt.ptrTypeSema(info: {
+ const result_ty = try pt.ptrType(info: {
var new = parent_ptr_ty.ptrInfo(zcu);
// We can correctly preserve alignment `.none`, since an error union has a
// natural alignment greater than or equal to that of its payload type.
@@ -2096,6 +1763,8 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
} }));
}
+// 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.
///
/// Returns a pointer to the aggregate field at the specified index.
@@ -2112,23 +1781,34 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
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.
- // `field_align` may be `.none` to represent the natural alignment of `field_ty`, but is not necessarily.
- const field_ty: Type, const field_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
+ 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, try aggregate_ty.fieldAlignmentSema(field_idx, pt) },
+ .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.
- try aggregate_ty.resolveLayout(pt);
const byte_off = aggregate_ty.structFieldOffset(field_idx, zcu);
- const field_align = a: {
+ 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 try aggregate_ty.abiAlignmentSema(pt);
+ break :pa aggregate_ty.abiAlignment(zcu);
} else parent_ptr_info.flags.alignment;
- break :a InternPool.Alignment.fromLog2Units(@min(parent_align.toLog2Units(), @ctz(byte_off)));
+ break :a .minStrict(true_field_align, parent_align);
};
- const result_ty = try pt.ptrTypeSema(info: {
+ const result_ty = try pt.ptrType(info: {
var new = parent_ptr_info;
new.child = field_ty.toIntern();
new.flags.alignment = field_align;
@@ -2143,7 +1823,7 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
new.packed_offset = packed_offset;
new.child = field_ty.toIntern();
if (new.flags.alignment == .none) {
- new.flags.alignment = try aggregate_ty.abiAlignmentSema(pt);
+ new.flags.alignment = aggregate_ty.abiAlignment(zcu);
}
break :info new;
});
@@ -2155,10 +1835,16 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
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, try aggregate_ty.fieldAlignmentSema(field_idx, pt) },
+ .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.ptrTypeSema(info: {
+ const result_ty = try pt.ptrType(info: {
var new = parent_ptr_info;
new.child = field_ty.toIntern();
break :info new;
@@ -2166,59 +1852,30 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
return pt.getCoerced(parent_ptr, result_ty);
},
.@"packed" => {
- // If the field has an ABI size matching its bit size, then we can continue to use a
- // non-bit pointer if the parent pointer is also a non-bit pointer.
- if (parent_ptr_info.packed_offset.host_size == 0 and (try field_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar * 8 == try field_ty.bitSizeSema(pt)) {
- // We must offset the pointer on big-endian targets, since the bits of packed memory don't align nicely.
- const byte_offset = switch (zcu.getTarget().cpu.arch.endian()) {
- .little => 0,
- .big => (try aggregate_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar - (try field_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar,
- };
- const result_ty = try pt.ptrTypeSema(info: {
- var new = parent_ptr_info;
- new.child = field_ty.toIntern();
- new.flags.alignment = InternPool.Alignment.fromLog2Units(
- @ctz(byte_offset | (try parent_ptr_ty.ptrAlignmentSema(pt)).toByteUnits().?),
- );
- break :info new;
- });
- return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt);
- } else {
- // The result must be a bit-pointer if it is not already.
- const result_ty = try pt.ptrTypeSema(info: {
- var new = parent_ptr_info;
- new.child = field_ty.toIntern();
- if (new.packed_offset.host_size == 0) {
- new.packed_offset.host_size = @intCast(((try aggregate_ty.bitSizeSema(pt)) + 7) / 8);
- assert(new.packed_offset.bit_offset == 0);
- }
- break :info new;
- });
- return pt.getCoerced(parent_ptr, result_ty);
- }
+ 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);
},
}
},
.pointer => field_ty: {
assert(aggregate_ty.isSlice(zcu));
- break :field_ty switch (field_idx) {
- Value.slice_ptr_index => .{ aggregate_ty.slicePtrFieldType(zcu), Type.usize.abiAlignment(zcu) },
- Value.slice_len_index => .{ Type.usize, Type.usize.abiAlignment(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),
+ } };
},
else => unreachable,
};
- const new_align: InternPool.Alignment = if (parent_ptr_info.flags.alignment != .none) a: {
- const ty_align = (try field_ty.abiAlignmentInner(.sema, zcu, pt.tid)).scalar;
- const true_field_align = if (field_align == .none) ty_align else field_align;
- const new_align = true_field_align.min(parent_ptr_info.flags.alignment);
- if (new_align == ty_align) break :a .none;
- break :a new_align;
- } else field_align;
-
- const result_ty = try pt.ptrTypeSema(info: {
+ const result_ty = try pt.ptrType(info: {
var new = parent_ptr_info;
new.child = field_ty.toIntern();
new.flags.alignment = new_align;
@@ -2241,6 +1898,7 @@ 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.
/// Returns a pointer to the element at the specified index.
/// May perform type resolution.
+/// MLUGG TODO AUDIT
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)) {
@@ -2267,21 +1925,19 @@ pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value
const strat: PtrStrat = switch (parent_ptr_ty.ptrSize(zcu)) {
.one => switch (elem_ty.zigTypeTag(zcu)) {
- .vector => .{ .offset = field_idx * @divExact(try elem_ty.childType(zcu).bitSizeSema(pt), 8) },
+ .vector => .{ .offset = field_idx * @divExact(elem_ty.childType(zcu).bitSize(zcu), 8) },
.array => strat: {
const arr_elem_ty = elem_ty.childType(zcu);
- if (try arr_elem_ty.comptimeOnlySema(pt)) {
- break :strat .{ .elem_ptr = arr_elem_ty };
- }
- break :strat .{ .offset = field_idx * (try arr_elem_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar };
+ 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 (try elem_ty.comptimeOnlySema(pt))
+ .many, .c => if (elem_ty.comptimeOnly(zcu))
.{ .elem_ptr = elem_ty }
else
- .{ .offset = field_idx * (try elem_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar },
+ .{ .offset = field_idx * elem_ty.abiSize(zcu) },
.slice => unreachable,
};
@@ -2430,6 +2086,7 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread) Al
/// 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 {
+ // MLUGG TODO: audit tf outta this code
const zcu = pt.zcu;
const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
const base_derive: PointerDeriveStep = switch (ptr.base_addr) {
@@ -2454,7 +2111,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
.comptime_alloc => |idx| base: {
const sema = opt_sema.?;
const alloc = sema.getComptimeAlloc(idx);
- const val = try alloc.val.intern(pt, sema.arena);
+ const val = try alloc.val.intern(pt, arena);
const ty = val.typeOf(zcu);
break :base .{ .comptime_alloc_ptr = .{
.idx = idx,
@@ -2492,24 +2149,14 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
const base_ptr = Value.fromInterned(field.base);
const base_ptr_ty = base_ptr.typeOf(zcu);
const agg_ty = base_ptr_ty.childType(zcu);
- const field_ty, const field_align = switch (agg_ty.zigTypeTag(zcu)) {
- .@"struct" => .{ agg_ty.fieldType(@intCast(field.index), zcu), try agg_ty.fieldAlignmentInner(
- @intCast(field.index),
- if (resolve_types) .sema else .normal,
- pt.zcu,
- if (resolve_types) pt.tid else {},
- ) },
- .@"union" => .{ agg_ty.unionFieldTypeByIndex(@intCast(field.index), zcu), try agg_ty.fieldAlignmentInner(
- @intCast(field.index),
- if (resolve_types) .sema else .normal,
- pt.zcu,
- if (resolve_types) pt.tid else {},
- ) },
- .pointer => .{ switch (field.index) {
- Value.slice_ptr_index => agg_ty.slicePtrFieldType(zcu),
- Value.slice_len_index => Type.usize,
+ if (resolve_types) try opt_sema.?.ensureLayoutResolved(agg_ty);
+ 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,
- }, Type.usize.abiAlignment(zcu) },
+ },
else => unreachable,
};
const base_align = base_ptr_ty.ptrAlignment(zcu);
@@ -2720,148 +2367,6 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh
} };
}
-pub fn resolveLazy(
- val: Value,
- arena: Allocator,
- pt: Zcu.PerThread,
-) Zcu.SemaError!Value {
- switch (pt.zcu.intern_pool.indexToKey(val.toIntern())) {
- .int => |int| switch (int.storage) {
- .u64, .i64, .big_int => return val,
- .lazy_align, .lazy_size => return pt.intValue(
- Type.fromInterned(int.ty),
- try val.toUnsignedIntSema(pt),
- ),
- },
- .slice => |slice| {
- const ptr = try Value.fromInterned(slice.ptr).resolveLazy(arena, pt);
- const len = try Value.fromInterned(slice.len).resolveLazy(arena, pt);
- if (ptr.toIntern() == slice.ptr and len.toIntern() == slice.len) return val;
- return Value.fromInterned(try pt.intern(.{ .slice = .{
- .ty = slice.ty,
- .ptr = ptr.toIntern(),
- .len = len.toIntern(),
- } }));
- },
- .ptr => |ptr| {
- switch (ptr.base_addr) {
- .nav, .comptime_alloc, .uav, .int => return val,
- .comptime_field => |field_val| {
- const resolved_field_val = (try Value.fromInterned(field_val).resolveLazy(arena, pt)).toIntern();
- return if (resolved_field_val == field_val)
- val
- else
- Value.fromInterned(try pt.intern(.{ .ptr = .{
- .ty = ptr.ty,
- .base_addr = .{ .comptime_field = resolved_field_val },
- .byte_offset = ptr.byte_offset,
- } }));
- },
- .eu_payload, .opt_payload => |base| {
- const resolved_base = (try Value.fromInterned(base).resolveLazy(arena, pt)).toIntern();
- return if (resolved_base == base)
- val
- else
- Value.fromInterned(try pt.intern(.{ .ptr = .{
- .ty = ptr.ty,
- .base_addr = switch (ptr.base_addr) {
- .eu_payload => .{ .eu_payload = resolved_base },
- .opt_payload => .{ .opt_payload = resolved_base },
- else => unreachable,
- },
- .byte_offset = ptr.byte_offset,
- } }));
- },
- .arr_elem, .field => |base_index| {
- const resolved_base = (try Value.fromInterned(base_index.base).resolveLazy(arena, pt)).toIntern();
- return if (resolved_base == base_index.base)
- val
- else
- Value.fromInterned(try pt.intern(.{ .ptr = .{
- .ty = ptr.ty,
- .base_addr = switch (ptr.base_addr) {
- .arr_elem => .{ .arr_elem = .{
- .base = resolved_base,
- .index = base_index.index,
- } },
- .field => .{ .field = .{
- .base = resolved_base,
- .index = base_index.index,
- } },
- else => unreachable,
- },
- .byte_offset = ptr.byte_offset,
- } }));
- },
- }
- },
- .aggregate => |aggregate| switch (aggregate.storage) {
- .bytes => return val,
- .elems => |elems| {
- var resolved_elems: []InternPool.Index = &.{};
- for (elems, 0..) |elem, i| {
- const resolved_elem = (try Value.fromInterned(elem).resolveLazy(arena, pt)).toIntern();
- if (resolved_elems.len == 0 and resolved_elem != elem) {
- resolved_elems = try arena.alloc(InternPool.Index, elems.len);
- @memcpy(resolved_elems[0..i], elems[0..i]);
- }
- if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem;
- }
- return if (resolved_elems.len == 0)
- val
- else
- pt.aggregateValue(.fromInterned(aggregate.ty), resolved_elems);
- },
- .repeated_elem => |elem| {
- const resolved_elem = try Value.fromInterned(elem).resolveLazy(arena, pt);
- return if (resolved_elem.toIntern() == elem)
- val
- else
- pt.aggregateSplatValue(.fromInterned(aggregate.ty), resolved_elem);
- },
- },
- .un => |un| {
- const resolved_tag = if (un.tag == .none)
- .none
- else
- (try Value.fromInterned(un.tag).resolveLazy(arena, pt)).toIntern();
- const resolved_val = (try Value.fromInterned(un.val).resolveLazy(arena, pt)).toIntern();
- return if (resolved_tag == un.tag and resolved_val == un.val)
- val
- else
- Value.fromInterned(try pt.internUnion(.{
- .ty = un.ty,
- .tag = resolved_tag,
- .val = resolved_val,
- }));
- },
- .error_union => |eu| switch (eu.val) {
- .err_name => return val,
- .payload => |payload| {
- const resolved_payload = try Value.fromInterned(payload).resolveLazy(arena, pt);
- if (resolved_payload.toIntern() == payload) return val;
- return .fromInterned(try pt.intern(.{ .error_union = .{
- .ty = eu.ty,
- .val = .{ .payload = resolved_payload.toIntern() },
- } }));
- },
- },
- .opt => |opt| switch (opt.val) {
- .none => return val,
- else => |payload| {
- const resolved_payload = try Value.fromInterned(payload).resolveLazy(arena, pt);
- if (resolved_payload.toIntern() == payload) return val;
- return .fromInterned(try pt.intern(.{ .opt = .{
- .ty = opt.ty,
- .val = resolved_payload.toIntern(),
- } }));
- },
- },
-
- else => return val,
- }
-}
-
const InterpretMode = enum {
/// In this mode, types are assumed to match what the compiler was built with in terms of field
/// order, field types, etc. This improves compiler performance. However, it means that certain
@@ -2878,7 +2383,6 @@ const interpret_mode: InterpretMode = @field(InterpretMode, @tagName(build_optio
/// Given a `Value` representing a comptime-known value of type `T`, unwrap it into an actual `T` known to the compiler.
/// This is useful for accessing `std.builtin` structures received from comptime logic.
-/// `val` must be fully resolved.
pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T {
const zcu = pt.zcu;
const io = zcu.comp.io;
@@ -2917,7 +2421,6 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe
},
.int => switch (ip.indexToKey(val.toIntern()).int.storage) {
- .lazy_align, .lazy_size => unreachable, // `val` is fully resolved
inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch,
.big_int => |big| big.toInt(T) catch return error.TypeMismatch,
},
diff --git a/src/Zcu.zig b/src/Zcu.zig
index f2d6dbf497a1f47faef87d208c082c364867bb9e..e1760fecb95c1871ca3229becbebc6d224e4173a 100644
--- a/src/Zcu.zig
+++ b/src/Zcu.zig
@@ -14,6 +14,8 @@ const mem = std.mem;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const log = std.log.scoped(.zcu);
+const deps_log = std.log.scoped(.zcu_deps);
+const refs_log = std.log.scoped(.zcu_refs);
const BigIntConst = std.math.big.int.Const;
const BigIntMutable = std.math.big.int.Mutable;
const Target = std.Target;
@@ -2685,10 +2687,10 @@ pub const LazySrcLoc = struct {
.struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_node,
.struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_node,
.extended => switch (inst.data.extended.opcode) {
- .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_node,
- .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_node,
- .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_node,
- .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_node,
+ .struct_decl => zir.getStructDecl(zir_inst).src_node,
+ .union_decl => zir.getUnionDecl(zir_inst).src_node,
+ .enum_decl => zir.getEnumDecl(zir_inst).src_node,
+ .opaque_decl => zir.getOpaqueDecl(zir_inst).src_node,
.reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node,
.reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node,
.reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node,
@@ -3063,7 +3065,7 @@ pub fn markDependeeOutdated(
marked_po: enum { not_marked_po, marked_po },
dependee: InternPool.Dependee,
) !void {
- log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
+ deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
@@ -3071,9 +3073,9 @@ pub fn markDependeeOutdated(
.not_marked_po => {},
.marked_po => {
po_dep_count.* -= 1;
- log.debug("outdated {f} => already outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
+ deps_log.debug("outdated {f} => already outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
if (po_dep_count.* == 0) {
- log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
+ deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
},
@@ -3094,9 +3096,9 @@ pub fn markDependeeOutdated(
depender,
new_po_dep_count,
);
- log.debug("outdated {f} => new outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count });
+ deps_log.debug("outdated {f} => new outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count });
if (new_po_dep_count == 0) {
- log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
+ deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
// If this is a Decl and was not previously PO, we must recursively
@@ -3109,16 +3111,16 @@ pub fn markDependeeOutdated(
}
pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
- log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)});
+ deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)});
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
// This depender is already outdated, but it now has one
// less PO dependency!
po_dep_count.* -= 1;
- log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
+ deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
if (po_dep_count.* == 0) {
- log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
+ deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
continue;
@@ -3132,11 +3134,11 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
};
if (ptr.* > 1) {
ptr.* -= 1;
- log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* });
+ deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* });
continue;
}
- log.debug("up-to-date {f} => {f} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) });
+ deps_log.debug("up-to-date {f} => {f} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) });
// This dependency is no longer PO, i.e. is known to be up-to-date.
assert(zcu.potentially_outdated.swapRemove(depender));
@@ -3146,8 +3148,9 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
.@"comptime" => {},
.nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
.nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }),
- .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
- .func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }),
+ .type_layout => |ty| try zcu.markPoDependeeUpToDate(.{ .type_layout = ty }),
+ .type_inits => |ty| try zcu.markPoDependeeUpToDate(.{ .type_inits = ty }),
+ .func => |func| try zcu.markPoDependeeUpToDate(.{ .func_ies = func }),
.memoized_state => |stage| try zcu.markPoDependeeUpToDate(.{ .memoized_state = stage }),
}
}
@@ -3161,11 +3164,12 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
.@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
.nav_val => |nav| .{ .nav_val = nav },
.nav_ty => |nav| .{ .nav_ty = nav },
- .type => |ty| .{ .interned = ty },
- .func => |func_index| .{ .interned = func_index }, // IES
+ .type_layout => |ty| .{ .type_layout = ty },
+ .type_inits => |ty| .{ .type_inits = ty },
+ .func => |func_index| .{ .func_ies = func_index },
.memoized_state => |stage| .{ .memoized_state = stage },
};
- log.debug("potentially outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
+ deps_log.debug("potentially outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
var it = ip.dependencyIterator(dependee);
while (it.next()) |po| {
if (zcu.outdated.getPtr(po)) |po_dep_count| {
@@ -3175,17 +3179,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
_ = zcu.outdated_ready.swapRemove(po);
}
po_dep_count.* += 1;
- log.debug("po {f} => {f} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* });
+ deps_log.debug("po {f} => {f} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* });
continue;
}
if (zcu.potentially_outdated.getPtr(po)) |n| {
// There is now one more PO dependency.
n.* += 1;
- log.debug("po {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* });
+ deps_log.debug("po {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* });
continue;
}
try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
- log.debug("po {f} => {f} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) });
+ deps_log.debug("po {f} => {f} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) });
// This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.
try zcu.markTransitiveDependersPotentiallyOutdated(po);
}
@@ -3240,13 +3244,15 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
var chosen_unit: ?AnalUnit = null;
var chosen_unit_dependers: u32 = undefined;
+ // MLUGG TODO: i'm 99% sure this is now impossible. check!!!
inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| {
for (outdated_units) |unit| {
var n: u32 = 0;
var it = ip.dependencyIterator(switch (unit.unwrap()) {
.func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
.@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice
- .type => |ty| .{ .interned = ty },
+ .type_layout => |ty| .{ .type_layout = ty },
+ .type_inits => |ty| .{ .type_inits = ty },
.nav_val => |nav| .{ .nav_val = nav },
.nav_ty => |nav| .{ .nav_ty = nav },
.memoized_state => {
@@ -3377,25 +3383,21 @@ pub fn mapOldZirToNew(
var comptime_decls: std.ArrayList(Zir.Inst.Index) = .empty;
defer comptime_decls.deinit(gpa);
- {
- var old_decl_it = old_zir.declIterator(match_item.old_inst);
- while (old_decl_it.next()) |old_decl_inst| {
- const old_decl = old_zir.getDeclaration(old_decl_inst);
- switch (old_decl.kind) {
- .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
- .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
- .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
- .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
- .@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
- }
+ for (old_zir.typeDecls(match_item.old_inst)) |old_decl_inst| {
+ const old_decl = old_zir.getDeclaration(old_decl_inst);
+ switch (old_decl.kind) {
+ .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
+ .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
+ .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
+ .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
+ .@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
}
}
var unnamed_test_idx: u32 = 0;
var comptime_decl_idx: u32 = 0;
- var new_decl_it = new_zir.declIterator(match_item.new_inst);
- while (new_decl_it.next()) |new_decl_inst| {
+ for (new_zir.typeDecls(match_item.new_inst)) |new_decl_inst| {
const new_decl = new_zir.getDeclaration(new_decl_inst);
// Attempt to match this to a declaration in the old ZIR:
// * For named declarations (`const`/`var`/`fn`), we match based on name.
@@ -3494,7 +3496,7 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !vo
}
try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
- try zcu.comp.queueJob(.{ .analyze_func = func_index });
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .func = func_index }) });
zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {});
}
@@ -3513,7 +3515,7 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void
}
try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
- try zcu.comp.queueJob(.{ .analyze_comptime_unit = .wrap(.{ .nav_val = nav_id }) });
+ try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .nav_val = nav_id }) });
zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {});
}
@@ -3908,8 +3910,7 @@ pub fn atomicPtrAlignment(
return error.BadType;
}
-/// Returns null in the following cases:
-/// * Not a struct.
+/// Returns null if `ty` is not a struct.
pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
if (ty.ip_index == .none) return null;
const ip = &zcu.intern_pool;
@@ -3936,7 +3937,6 @@ pub fn structPackedFieldBitOffset(
) u16 {
const ip = &zcu.intern_pool;
assert(struct_type.layout == .@"packed");
- assert(struct_type.haveLayout(ip));
var bit_sum: u64 = 0;
for (0..struct_type.field_types.len) |i| {
if (i == field_index) {
@@ -3995,8 +3995,10 @@ pub const UnionLayout = struct {
pub fn unionTagFieldIndex(zcu: *const Zcu, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
const ip = &zcu.intern_pool;
if (enum_tag.toIntern() == .none) return null;
- assert(ip.typeOf(enum_tag.toIntern()) == loaded_union.enum_tag_ty);
- return loaded_union.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern());
+ const enum_tag_key = ip.indexToKey(enum_tag.toIntern()).enum_tag;
+ assert(enum_tag_key.ty == loaded_union.enum_tag_type);
+ const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type);
+ return loaded_enum.tagValueIndex(ip, enum_tag_key.int);
}
pub const ResolvedReference = struct {
@@ -4049,31 +4051,36 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const referencer = types.values()[type_idx];
type_idx += 1;
- log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
+ refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
- // If this type undergoes type resolution, the corresponding `AnalUnit` is automatically referenced.
- const has_resolution: bool = switch (ip.indexToKey(ty)) {
- .struct_type, .union_type => true,
- .enum_type => |k| k != .generated_tag,
- .opaque_type => false,
+ // If this type undergoes type resolution, the corresponding `AnalUnit`s are automatically referenced.
+ const has_layout: bool, const has_inits: bool = switch (ip.indexToKey(ty)) {
+ .struct_type => .{ true, true },
+ .union_type => .{ true, false },
+ .enum_type => .{ false, true },
+ .opaque_type => .{ false, false },
else => unreachable,
};
- if (has_resolution) {
+ if (has_layout) {
// this should only be referenced by the type
- const unit: AnalUnit = .wrap(.{ .type = ty });
+ const unit: AnalUnit = .wrap(.{ .type_layout = ty });
+ try units.putNoClobber(gpa, unit, referencer);
+ }
+ if (has_inits) {
+ // this should only be referenced by the type
+ const unit: AnalUnit = .wrap(.{ .type_inits = ty });
try units.putNoClobber(gpa, unit, referencer);
}
// If this is a union with a generated tag, its tag type is automatically referenced.
// We don't add this reference for non-generated tags, as those will already be referenced via the union's type resolution, with a better source location.
- if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| {
- const tag_ty = union_obj.enum_tag_ty;
- if (tag_ty != .none) {
- if (ip.indexToKey(tag_ty).enum_type == .generated_tag) {
- const gop = try types.getOrPut(gpa, tag_ty);
- if (!gop.found_existing) gop.value_ptr.* = referencer;
- }
- }
+ implicit_tag: {
+ const loaded_union = zcu.typeToUnion(.fromInterned(ty)) orelse break :implicit_tag;
+ const tag_ty = loaded_union.enum_tag_type;
+ if (ip.indexToKey(tag_ty).enum_type != .generated_union_tag) break :implicit_tag;
+ const gop = try types.getOrPut(gpa, tag_ty);
+ if (gop.found_existing) break :implicit_tag;
+ gop.value_ptr.* = referencer;
}
// Queue any decls within this type which would be automatically analyzed.
@@ -4084,7 +4091,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
const gop = try units.getOrPut(gpa, unit);
if (!gop.found_existing) {
- log.debug("type '{f}': ref comptime %{}", .{
+ refs_log.debug("type '{f}': ref comptime %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue),
});
@@ -4118,7 +4125,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
{
const gop = try units.getOrPut(gpa, .wrap(.{ .nav_val = nav_id }));
if (!gop.found_existing) {
- log.debug("type '{f}': ref test %{}", .{
+ refs_log.debug("type '{f}': ref test %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -4141,7 +4148,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
const gop = try units.getOrPut(gpa, unit);
if (!gop.found_existing) {
- log.debug("type '{f}': ref named %{}", .{
+ refs_log.debug("type '{f}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -4158,7 +4165,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
const gop = try units.getOrPut(gpa, unit);
if (!gop.found_existing) {
- log.debug("type '{f}': ref named %{}", .{
+ refs_log.debug("type '{f}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -4177,14 +4184,14 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const other: AnalUnit = .wrap(switch (unit.unwrap()) {
.nav_val => |n| .{ .nav_ty = n },
.nav_ty => |n| .{ .nav_val = n },
- .@"comptime", .type, .func, .memoized_state => break :queue_paired,
+ .@"comptime", .type_layout, .type_inits, .func, .memoized_state => break :queue_paired,
});
const gop = try units.getOrPut(gpa, other);
if (gop.found_existing) break :queue_paired;
gop.value_ptr.* = units.values()[unit_idx]; // same reference location
}
- log.debug("handle unit '{f}'", .{zcu.fmtAnalUnit(unit)});
+ refs_log.debug("handle unit '{f}'", .{zcu.fmtAnalUnit(unit)});
if (zcu.reference_table.get(unit)) |first_ref_idx| {
assert(first_ref_idx != std.math.maxInt(u32));
@@ -4193,7 +4200,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const ref = zcu.all_references.items[ref_idx];
const gop = try units.getOrPut(gpa, ref.referenced);
if (!gop.found_existing) {
- log.debug("unit '{f}': ref unit '{f}'", .{
+ refs_log.debug("unit '{f}': ref unit '{f}'", .{
zcu.fmtAnalUnit(unit),
zcu.fmtAnalUnit(ref.referenced),
});
@@ -4213,7 +4220,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
const ref = zcu.all_type_references.items[ref_idx];
const gop = try types.getOrPut(gpa, ref.referenced);
if (!gop.found_existing) {
- log.debug("unit '{f}': ref type '{f}'", .{
+ refs_log.debug("unit '{f}': ref type '{f}'", .{
zcu.fmtAnalUnit(unit),
Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip),
});
@@ -4323,9 +4330,8 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void
return writer.print("comptime(inst= [{}])", .{@intFromEnum(cu_id)});
}
},
- .nav_val => |nav| return writer.print("nav_val('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
- .nav_ty => |nav| return writer.print("nav_ty('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
- .type => |ty| return writer.print("ty('{f}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),
+ .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
+ .type_layout, .type_inits => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),
.func => |func| {
const nav = zcu.funcInfo(func).owner_nav;
return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) });
@@ -4347,18 +4353,17 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void
const file_path = zcu.fileByIndex(info.file).path;
return writer.print("inst('{f}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) });
},
- .nav_val => |nav| {
+ .nav_val, .nav_ty => |nav, tag| {
const fqn = ip.getNav(nav).fqn;
- return writer.print("nav_val('{f}')", .{fqn.fmt(ip)});
+ return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) });
},
- .nav_ty => |nav| {
- const fqn = ip.getNav(nav).fqn;
- return writer.print("nav_ty('{f}')", .{fqn.fmt(ip)});
+ .type_layout, .type_inits => |ip_index, tag| {
+ const name = Type.fromInterned(ip_index).containerTypeName(ip);
+ return writer.print("{t}('{f}')", .{ tag, name.fmt(ip) });
},
- .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
- .struct_type, .union_type, .enum_type => return writer.print("type('{f}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
- .func => |f| return writer.print("ies('{f}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
- else => unreachable,
+ .func_ies => |ip_index| {
+ const fqn = ip.getNav(ip.indexToKey(ip_index).func.owner_nav).fqn;
+ return writer.print("func_ies('{f}')", .{fqn.fmt(ip)});
},
.zon_file => |file| {
const file_path = zcu.fileByIndex(file).path;
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
index 5472afc5f02682b3f9083b4d710e1c5616c74d57..f9faef2f7a36f0d77cbf86db7c7e355db8038056 100644
--- a/src/Zcu/PerThread.zig
+++ b/src/Zcu/PerThread.zig
@@ -598,44 +598,38 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
// Value is whether the declaration is `pub`.
var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, bool) = .empty;
defer old_names.deinit(zcu.gpa);
- {
- var it = old_zir.declIterator(old_inst);
- while (it.next()) |decl_inst| {
- const old_decl = old_zir.getDeclaration(decl_inst);
- if (old_decl.name == .empty) continue;
- const name_ip = try zcu.intern_pool.getOrPutString(
- zcu.gpa,
- io,
- pt.tid,
- old_zir.nullTerminatedString(old_decl.name),
- .no_embedded_nulls,
- );
- try old_names.put(zcu.gpa, name_ip, old_decl.is_pub);
- }
+ for (old_zir.typeDecls(old_inst)) |decl_inst| {
+ const old_decl = old_zir.getDeclaration(decl_inst);
+ if (old_decl.name == .empty) continue;
+ const name_ip = try zcu.intern_pool.getOrPutString(
+ zcu.gpa,
+ io,
+ pt.tid,
+ old_zir.nullTerminatedString(old_decl.name),
+ .no_embedded_nulls,
+ );
+ try old_names.put(zcu.gpa, name_ip, old_decl.is_pub);
}
var any_change = false;
- {
- var it = new_zir.declIterator(new_inst);
- while (it.next()) |decl_inst| {
- const new_decl = new_zir.getDeclaration(decl_inst);
- if (new_decl.name == .empty) continue;
- const name_ip = try zcu.intern_pool.getOrPutString(
- zcu.gpa,
- io,
- pt.tid,
- new_zir.nullTerminatedString(new_decl.name),
- .no_embedded_nulls,
- );
- if (old_names.fetchSwapRemove(name_ip)) |kv| {
- if (kv.value == new_decl.is_pub) continue;
- }
- // Name added, or changed whether it's pub
- any_change = true;
- try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
- .namespace = tracked_inst_index,
- .name = name_ip,
- } });
+ for (new_zir.typeDecls(new_inst)) |decl_inst| {
+ const new_decl = new_zir.getDeclaration(decl_inst);
+ if (new_decl.name == .empty) continue;
+ const name_ip = try zcu.intern_pool.getOrPutString(
+ zcu.gpa,
+ io,
+ pt.tid,
+ new_zir.nullTerminatedString(new_decl.name),
+ .no_embedded_nulls,
+ );
+ if (old_names.fetchSwapRemove(name_ip)) |kv| {
+ if (kv.value == new_decl.is_pub) continue;
}
+ // Name added, or changed whether it's pub
+ any_change = true;
+ try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
+ .namespace = tracked_inst_index,
+ .name = name_ip,
+ } });
}
// The only elements remaining in `old_names` now are any names which were removed.
for (old_names.keys()) |name_ip| {
@@ -674,24 +668,49 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
}
}
-/// Ensures that `zcu.fileRootType` on this `file_index` gives an up-to-date answer.
-/// Returns `error.AnalysisFail` if the file has an error.
-pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
- const file_root_type = pt.zcu.fileRootType(file_index);
- if (file_root_type != .none) {
- if (pt.ensureTypeUpToDate(file_root_type)) |_| {
- return;
- } else |err| switch (err) {
- error.AnalysisFail => {
- // The file's root `struct_decl` has, at some point, been lost, because the file failed AstGen.
- // Clear `file_root_type`, and try the `semaFile` call below, in case the instruction has since
- // been discovered under a new `TrackedInst.Index`.
- pt.zcu.setFileRootType(file_index, .none);
- },
- else => |e| return e,
- }
- }
- return pt.semaFile(file_index);
+/// Ensures that `zcu.fileRootType` on this `file_index` is populated (not `.none`). This implies
+/// that the file's namespace is scanned, discovering declarations.
+///
+/// Typical Zig compilations begin by claling this function on the root source file of the standard
+/// library, `lib/std/std.zig`. The resulting namespace scan discovers a `comptime` declaration in
+/// that file, which is queued for analysis, and everything goes from there.
+pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) (Allocator.Error || Io.Cancelable)!void {
+ dev.check(.sema);
+
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const io = comp.io;
+ const gpa = comp.gpa;
+ const ip = &zcu.intern_pool;
+
+ if (zcu.fileRootType(file_index) != .none) return; // already good
+
+ const file = zcu.fileByIndex(file_index);
+ assert(file.getMode() == .zig);
+ const struct_decl = file.zir.?.getStructDecl(.main_struct_inst);
+ const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{
+ .file = file_index,
+ .inst = .main_struct_inst,
+ });
+ const file_root_type = try Sema.analyzeStructDecl(
+ pt,
+ file_index,
+ &file.zir.?,
+ .none,
+ tracked_inst,
+ &struct_decl,
+ null,
+ &.{},
+ .{ .exact = .{
+ .name = try file.internFullyQualifiedName(pt),
+ .nav = .none,
+ } },
+ );
+ zcu.setFileRootType(file_index, file_root_type.toIntern());
+ if (zcu.comp.time_report) |*tr| tr.stats.n_imported_files += 1;
}
/// Ensures that all memoized state on `Zcu` is up-to-date, performing re-analysis if necessary.
@@ -1012,6 +1031,238 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
try sema.flushExports();
}
+/// Ensures that the layout of the given `struct` or `union` type is fully up-to-date, performing
+/// re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!) or union. Returns
+/// `error.AnalysisFail` if an analysis error is encountered during type resolution; the caller is
+/// free to ignore this, since the error is already registered.
+pub fn ensureTypeLayoutUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+
+ const anal_unit: AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });
+
+ log.debug("ensureTypeLayoutUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ assert(!zcu.analysis_in_progress.contains(anal_unit));
+
+ // Determine whether or not this type is outdated. For this kind of `AnalUnit`, that's
+ // the only indicator as to whether or not analysis is required; when a struct/union is
+ // first created, it's marked as outdated.
+ // MLUGG TODO: make that actually true, it's a good strategy here!
+
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ if (was_outdated) {
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
+ if (dev.env.supports(.incremental)) {
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ zcu.deleteUnitCompileLogs(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+ }
+ // For types, we already know that we have to invalidate all dependees.
+ // TODO: we actually *could* detect whether everything was the same. should we bother?
+ try zcu.markDependeeOutdated(.marked_po, .{ .type_layout = ty.toIntern() });
+ } else {
+ // We can trust the current information about this unit.
+ if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ return;
+ }
+
+ if (zcu.comp.debugIncremental()) {
+ const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
+ info.last_update_gen = zcu.generation;
+ info.deps.clearRetainingCapacity();
+ }
+
+ const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(&zcu.intern_pool).toSlice(&zcu.intern_pool), null);
+ defer unit_tracking.end(zcu);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ const file = zcu.namespacePtr(ty.getNamespaceIndex(zcu)).fileScope(zcu);
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = file.zir.?,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
+
+ const result = switch (ty.containerLayout(zcu)) {
+ .auto, .@"extern" => switch (ty.zigTypeTag(zcu)) {
+ .@"struct" => Sema.type_resolution.resolveStructLayout(&sema, ty),
+ .@"union" => Sema.type_resolution.resolveUnionLayout(&sema, ty),
+ else => unreachable,
+ },
+ .@"packed" => switch (ty.zigTypeTag(zcu)) {
+ .@"struct" => Sema.type_resolution.resolvePackedStructLayout(&sema, ty),
+ .@"union" => Sema.type_resolution.resolvePackedUnionLayout(&sema, ty),
+ else => unreachable,
+ },
+ };
+ result catch |err| switch (err) {
+ error.AnalysisFail => {
+ if (!zcu.failed_analysis.contains(anal_unit)) {
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
+ // Since it does not, this must be a transitive failure.
+ try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
+ }
+ return error.AnalysisFail;
+ },
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
+ };
+
+ sema.flushExports() catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ };
+
+ codegen_type: {
+ if (zcu.comp.config.use_llvm) break :codegen_type;
+ if (file.mod.?.strip) break :codegen_type;
+ zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
+ try zcu.comp.queueJob(.{ .link_type = ty.toIntern() });
+ }
+}
+
+/// Ensures that the default/tag values of the given `struct` or `enum` type are fully up-to-date,
+/// performing re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!) or an enum.
+/// Returns `error.AnalysisFail` if an analysis error is encountered during resolution; the caller
+/// is free to ignore this, since the error is already registered.
+pub fn ensureTypeInitsUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+
+ const anal_unit: AnalUnit = .wrap(.{ .type_inits = ty.toIntern() });
+
+ log.debug("ensureTypeInitsUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ assert(!zcu.analysis_in_progress.contains(anal_unit));
+
+ // Determine whether or not this type is outdated. For this kind of `AnalUnit`, that's
+ // the only indicator as to whether or not analysis is required; when a struct/enum is
+ // first created, it's marked as outdated.
+ // MLUGG TODO: make that actually true, it's a good strategy here!
+
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ if (was_outdated) {
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
+ if (dev.env.supports(.incremental)) {
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ zcu.deleteUnitCompileLogs(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+ }
+ // For types, we already know that we have to invalidate all dependees.
+ // TODO: we actually *could* detect whether everything was the same. should we bother?
+ try zcu.markDependeeOutdated(.marked_po, .{ .type_inits = ty.toIntern() });
+ } else {
+ // We can trust the current information about this unit.
+ if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ return;
+ }
+
+ if (zcu.comp.debugIncremental()) {
+ const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
+ info.last_update_gen = zcu.generation;
+ info.deps.clearRetainingCapacity();
+ }
+
+ const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(&zcu.intern_pool).toSlice(&zcu.intern_pool), null);
+ defer unit_tracking.end(zcu);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ const zir = zcu.namespacePtr(ty.getNamespaceIndex(zcu)).fileScope(zcu).zir.?;
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
+
+ const result = switch (ty.zigTypeTag(zcu)) {
+ .@"struct" => Sema.type_resolution.resolveStructDefaults(&sema, ty),
+ .@"enum" => Sema.type_resolution.resolveEnumValues(&sema, ty),
+ else => unreachable,
+ };
+ result catch |err| switch (err) {
+ error.AnalysisFail => {
+ if (!zcu.failed_analysis.contains(anal_unit)) {
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
+ // Since it does not, this must be a transitive failure.
+ try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
+ }
+ return error.AnalysisFail;
+ },
+ error.OutOfMemory,
+ error.Canceled,
+ => |e| return e,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
+ };
+
+ sema.flushExports() catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ };
+}
+
/// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis
/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
/// free to ignore this, since the error is already registered.
@@ -1360,7 +1611,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
// This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type,
// this resolves the type `type` (which needs no resolution), not the struct itself.
- try nav_ty.resolveLayout(pt);
+ try sema.ensureLayoutResolved(nav_ty);
const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
.func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
@@ -1377,7 +1628,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) {
return sema.fail(&block, align_src, "target does not support function alignment", .{});
}
- } else if (try nav_ty.comptimeOnlySema(pt)) {
+ } else if (nav_ty.comptimeOnly(zcu)) {
// alignment, linksection, addrspace annotations are not allowed for comptime-only types.
const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) {
.func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
@@ -1420,12 +1671,11 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
queue_codegen: {
if (!queue_linker_work) break :queue_codegen;
- if (!try nav_ty.hasRuntimeBitsSema(pt)) {
+ if (!nav_ty.hasRuntimeBits(zcu)) {
if (zcu.comp.config.use_llvm) break :queue_codegen;
if (file.mod.?.strip) break :queue_codegen;
}
- // This job depends on any resolve_type_fully jobs queued up before it.
zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
try zcu.comp.queueJob(.{ .link_nav = nav_id });
}
@@ -1628,7 +1878,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
break :ty .fromInterned(type_ref.toInterned().?);
};
- try resolved_ty.resolveLayout(pt);
+ try sema.ensureLayoutResolved(resolved_ty);
// In the case where the type is specified, this function is also responsible for resolving
// the pointer modifiers, i.e. alignment, linksection, addrspace.
@@ -1765,9 +2015,9 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z
if (was_outdated) {
if (ies_outdated) {
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
+ try zcu.markDependeeOutdated(.marked_po, .{ .func_ies = func_index });
} else {
- try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
+ try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index });
}
}
@@ -1817,7 +2067,7 @@ fn analyzeFuncBody(
log.debug("analyze and generate fn body {f}", .{zcu.fmtAnalUnit(anal_unit)});
- var air = try pt.analyzeFnBodyInner(func_index);
+ var air = try pt.analyzeFuncBodyInner(func_index);
errdefer air.deinit(gpa);
const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or
@@ -1833,7 +2083,6 @@ fn analyzeFuncBody(
return .{ .ies_outdated = ies_outdated };
}
- // This job depends on any resolve_type_fully jobs queued up before it.
zcu.codegen_prog_node.increaseEstimatedTotalItems(1);
comp.link_prog_node.increaseEstimatedTotalItems(1);
try comp.queueJob(.{ .codegen_func = .{
@@ -1844,94 +2093,12 @@ fn analyzeFuncBody(
return .{ .ies_outdated = ies_outdated };
}
-pub fn semaMod(pt: Zcu.PerThread, mod: *Module) !void {
- dev.check(.sema);
- const file_index = pt.zcu.module_roots.get(mod).?.unwrap().?;
- const root_type = pt.zcu.fileRootType(file_index);
- if (root_type == .none) {
- return pt.semaFile(file_index);
- }
-}
-
-fn createFileRootStruct(
- pt: Zcu.PerThread,
- file_index: Zcu.File.Index,
- namespace_index: Zcu.Namespace.Index,
- replace_existing: bool,
-) Allocator.Error!InternPool.Index {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const io = zcu.comp.io;
- const ip = &zcu.intern_pool;
- const file = zcu.fileByIndex(file_index);
- const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- assert(!small.has_captures_len);
- assert(!small.has_backing_int);
- assert(small.layout == .auto);
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = file.zir.?.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = file.zir.?.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- const decls = file.zir.?.bodySlice(extra_index, decls_len);
- extra_index += decls_len;
-
- const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{
- .file = file_index,
- .inst = .main_struct_inst,
- });
- const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
- .layout = .auto,
- .fields_len = fields_len,
- .known_non_opv = small.known_non_opv,
- .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
- .any_comptime_fields = small.any_comptime_fields,
- .any_default_inits = small.any_default_inits,
- .inits_resolved = false,
- .any_aligned_fields = small.any_aligned_fields,
- .key = .{ .declared = .{
- .zir_index = tracked_inst,
- .captures = &.{},
- } },
- }, replace_existing)) {
- .existing => unreachable, // we wouldn't be analysing the file root if this type existed
- .wip => |wip| wip,
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none);
- ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
-
- if (zcu.comp.config.incremental) {
- try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
- }
-
- try pt.scanNamespace(namespace_index, decls);
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- codegen_type: {
- if (file.mod.?.strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
- zcu.setFileRootType(file_index, wip_ty.index);
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- return wip_ty.finish(ip, namespace_index);
-}
-
/// Re-scan the namespace of a file's root struct type on an incremental update.
/// The file must have successfully populated ZIR.
/// If the file's root struct type is not populated (the file is unreferenced), nothing is done.
/// This is called by `updateZirRefs` for all updated files before the main work loop.
/// This function does not perform any semantic analysis.
+/// MLUGG TODO: mmmmm i have no idea if this makes sense... tbhwy i just want to update all *changed* namespaces at the start of an update or something lol
fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
const zcu = pt.zcu;
@@ -1945,48 +2112,11 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
});
const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
- const decls = decls: {
- const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
-
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
- extra_index += @intFromBool(small.has_fields_len);
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = file.zir.?.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- break :decls file.zir.?.bodySlice(extra_index, decls_len);
- };
+ const decls = file.zir.?.getStructDecl(.main_struct_inst).decls;
try pt.scanNamespace(namespace_index, decls);
zcu.namespacePtr(namespace_index).generation = zcu.generation;
}
-fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const zcu = pt.zcu;
- const file = zcu.fileByIndex(file_index);
- assert(file.getMode() == .zig);
- assert(zcu.fileRootType(file_index) == .none);
-
- assert(file.zir != null);
-
- const new_namespace_index = try pt.createNamespace(.{
- .parent = .none,
- .owner_type = undefined, // set in `createFileRootStruct`
- .file_scope = file_index,
- .generation = zcu.generation,
- });
- const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
- errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
-
- if (zcu.comp.time_report) |*tr| {
- tr.stats.n_imported_files += 1;
- }
-}
-
/// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is
/// then responsible for queueing a new AstGen job for the new file.
/// Assumes that `comp.mutex` is NOT locked. It will be locked by this function where necessary.
@@ -2878,15 +3008,15 @@ const ScanDeclIter = struct {
if (existing_unit == null and (want_analysis or decl.linkage == .@"export")) {
log.debug(
- "scanDecl queue analyze_comptime_unit file='{s}' unit={f}",
+ "scanDecl queue analyze_unit file='{s}' unit={f}",
.{ namespace.fileScope(zcu).sub_file_path, zcu.fmtAnalUnit(unit) },
);
- try comp.queueJob(.{ .analyze_comptime_unit = unit });
+ try comp.queueJob(.{ .analyze_unit = unit });
}
}
};
-fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
+fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
const tracy = trace(@src());
defer tracy.end();
@@ -3020,16 +3150,12 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
const gop = sema.inst_map.getOrPutAssumeCapacity(inst);
if (gop.found_existing) continue; // provided above by comptime arg
- const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
+ const param_ty: Type = .fromInterned(fn_ty_info.param_types.get(ip)[runtime_param_index]);
runtime_param_index += 1;
- const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) {
- error.ComptimeReturn => unreachable,
- error.ComptimeBreak => unreachable,
- else => |e| return e,
- };
- if (opt_opv) |opv| {
- gop.value_ptr.* = Air.internedToRef(opv.toIntern());
+ try sema.ensureLayoutResolved(param_ty);
+ if (try param_ty.onePossibleValue(pt)) |opv| {
+ gop.value_ptr.* = .fromValue(opv);
continue;
}
const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
@@ -3038,12 +3164,14 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
sema.air_instructions.appendAssumeCapacity(.{
.tag = .arg,
.data = .{ .arg = .{
- .ty = Air.internedToRef(param_ty),
+ .ty = .fromIntern(param_ty.toIntern()),
.zir_param_index = @intCast(zir_param_index),
} },
});
}
+ try sema.ensureLayoutResolved(sema.fn_ret_ty);
+
const last_arg_index = inner_block.instructions.items.len;
// Save the error trace as our first action in the function.
@@ -3103,21 +3231,9 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
func.setResolvedErrorSet(ip, io, ies.resolved);
}
+ // MLUGG TODO: i think this can go away and the assert move to the defer?
assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- // Finally we must resolve the return type and parameter types so that backends
- // have full access to type information.
- // Crucially, this happens *after* we set the function state to success above,
- // so that dependencies on the function body will now be satisfied rather than
- // result in circular dependency errors.
- // TODO: this can go away once we fix backends having to resolve `StackTrace`.
- // The codegen timing guarantees that the parameter types will be populated.
- sema.resolveFnTypes(fn_ty, inner_block.nodeOffset(.zero)) catch |err| switch (err) {
- error.ComptimeReturn => unreachable,
- error.ComptimeBreak => unreachable,
- else => |e| return e,
- };
-
try sema.flushExports();
defer {
@@ -3605,16 +3721,6 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!
if (info.flags.size == .c) canon_info.flags.is_allowzero = true;
- // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee
- // type, we change it to 0 here. If this causes an assertion trip because the
- // pointee type needs to be resolved more, that needs to be done before calling
- // this ptr() function.
- if (info.flags.alignment != .none and
- info.flags.alignment == Type.fromInterned(info.child).abiAlignment(pt.zcu))
- {
- canon_info.flags.alignment = .none;
- }
-
switch (info.flags.vector_index) {
// Canonicalize host_size. If it matches the bit size of the pointee type,
// we change it to 0 here. If this causes an assertion trip, the pointee type
@@ -3632,16 +3738,6 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!
return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
}
-/// Like `ptrType`, but if `info` specifies an `alignment`, first ensures the pointer
-/// child type's alignment is resolved so that an invalid alignment is not used.
-/// In general, prefer this function during semantic analysis.
-pub fn ptrTypeSema(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Zcu.SemaError!Type {
- if (info.flags.alignment != .none) {
- _ = try Type.fromInterned(info.child).abiAlignmentSema(pt);
- }
- return pt.ptrType(info);
-}
-
pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {
return pt.ptrType(.{ .child = child_type.toIntern() });
}
@@ -3739,31 +3835,37 @@ pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: InternPool.Index) Allocat
/// declaration order.
pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Allocator.Error!Value {
const ip = &pt.zcu.intern_pool;
+ ty.assertHasInits(pt.zcu);
const enum_type = ip.loadEnumType(ty.toIntern());
- if (enum_type.values.len == 0) {
+ assert(field_index < enum_type.field_names.len);
+
+ if (enum_type.field_values.len == 0) {
// Auto-numbered fields.
return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
.ty = ty.toIntern(),
.int = try pt.intern(.{ .int = .{
- .ty = enum_type.tag_ty,
+ .ty = enum_type.int_tag_type,
.storage = .{ .u64 = field_index },
} }),
} }));
}
- return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
+ return .fromInterned(try pt.intern(.{ .enum_tag = .{
.ty = ty.toIntern(),
- .int = enum_type.values.get(ip)[field_index],
+ .int = enum_type.field_values.get(ip)[field_index],
} }));
}
pub fn undefValue(pt: Zcu.PerThread, ty: Type) Allocator.Error!Value {
- return Value.fromInterned(try pt.intern(.{ .undef = ty.toIntern() }));
+ if (std.debug.runtime_safety) {
+ assert(try ty.onePossibleValue(pt) == null);
+ }
+ return .fromInterned(try pt.intern(.{ .undef = ty.toIntern() }));
}
pub fn undefRef(pt: Zcu.PerThread, ty: Type) Allocator.Error!Air.Inst.Ref {
- return Air.internedToRef((try pt.undefValue(ty)).toIntern());
+ return .fromValue(try pt.undefValue(ty));
}
pub fn intValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value {
@@ -3916,7 +4018,7 @@ pub fn intFittingRange(pt: Zcu.PerThread, min: Value, max: Value) !Type {
assert(Value.order(min, max, zcu).compare(.lte));
}
- const sign = min.orderAgainstZero(zcu) == .lt;
+ const sign = min.compareHetero(.lt, .zero_comptime_int, zcu);
const min_val_bits = pt.intBitsForValue(min, sign);
const max_val_bits = pt.intBitsForValue(max, sign);
@@ -3955,12 +4057,6 @@ pub fn intBitsForValue(pt: Zcu.PerThread, val: Value, sign: bool) u16 {
return @as(u16, @intCast(big.bitCountTwosComp()));
},
- .lazy_align => |lazy_ty| {
- return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(pt.zcu).toByteUnits() orelse 0) + @intFromBool(sign);
- },
- .lazy_size => |lazy_ty| {
- return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(pt.zcu)) + @intFromBool(sign);
- },
}
}
@@ -3993,7 +4089,6 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!
const comp = zcu.comp;
const result = try zcu.intern_pool.getExtern(comp.gpa, comp.io, pt.tid, key);
if (result.new_nav.unwrap()) |nav| {
- // This job depends on any resolve_type_fully jobs queued up before it.
comp.link_prog_node.increaseEstimatedTotalItems(1);
try comp.queueJob(.{ .link_nav = nav });
if (comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
@@ -4013,367 +4108,6 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo
return ty.abiAlignment(zcu);
}
-/// `ty` is a container type requiring resolution (struct, union, or enum).
-/// If `ty` is outdated, it is recreated at a new `InternPool.Index`, which is returned.
-/// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned.
-/// If `ty` is not outdated, that same `InternPool.Index` is returned.
-/// If `ty` has already been replaced by this function, the new index will not be returned again.
-/// Also, if `ty` is an enum, this function will resolve the new type if needed, and the call site
-/// is responsible for checking `[transitive_]failed_analysis` to detect resolution failures.
-pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
-
- const anal_unit: AnalUnit = .wrap(.{ .type = ty });
- const outdated = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
-
- if (outdated) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
-
- const ty_key = switch (ip.indexToKey(ty)) {
- .struct_type, .union_type, .enum_type => |key| key,
- else => unreachable,
- };
- const declared_ty_key = switch (ty_key) {
- .reified => unreachable, // never outdated
- .generated_tag => unreachable, // never outdated
- .declared => |d| d,
- };
-
- if (declared_ty_key.zir_index.resolve(ip) == null) {
- // The instruction has been lost -- this type is dead.
- return error.AnalysisFail;
- }
-
- if (!outdated) return ty;
-
- // We will recreate the type at a new `InternPool.Index`.
-
- // Delete old state which is no longer in use. Technically, this is not necessary: these exports,
- // references, etc, will be ignored because the type itself is unreferenced. However, it allows
- // reusing the memory which is currently being used to track this state.
- zcu.deleteUnitExports(anal_unit);
- zcu.deleteUnitReferences(anal_unit);
- zcu.deleteUnitCompileLogs(anal_unit);
- if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
- kv.value.destroy(gpa);
- }
- _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
- zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
-
- if (zcu.comp.debugIncremental()) {
- const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
- info.last_update_gen = zcu.generation;
- info.deps.clearRetainingCapacity();
- }
-
- switch (ip.indexToKey(ty)) {
- .struct_type => return pt.recreateStructType(ty, declared_ty_key),
- .union_type => return pt.recreateUnionType(ty, declared_ty_key),
- .enum_type => return pt.recreateEnumType(ty, declared_ty_key),
- else => unreachable,
- }
-}
-
-fn recreateStructType(
- pt: Zcu.PerThread,
- old_ty: InternPool.Index,
- key: InternPool.Key.NamespaceType.Declared,
-) Allocator.Error!InternPool.Index {
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const inst_info = key.zir_index.resolveFull(ip).?;
- const file = zcu.fileByIndex(inst_info.file);
- const zir = file.zir.?;
-
- assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
- const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
- var extra_index = extra.end;
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew`
-
- const struct_obj = ip.loadStructType(old_ty);
-
- const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
- .layout = small.layout,
- .fields_len = fields_len,
- .known_non_opv = small.known_non_opv,
- .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
- .any_comptime_fields = small.any_comptime_fields,
- .any_default_inits = small.any_default_inits,
- .inits_resolved = false,
- .any_aligned_fields = small.any_aligned_fields,
- .key = .{ .declared_owned_captures = .{
- .zir_index = key.zir_index,
- .captures = key.captures.owned,
- } },
- }, true)) {
- .wip => |wip| wip,
- .existing => unreachable, // we passed `replace_existing`
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- wip_ty.setName(ip, struct_obj.name, struct_obj.name_nav);
- try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index });
- zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index;
- // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive.
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
-
- codegen_type: {
- if (file.mod.?.strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
-
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- const new_ty = wip_ty.finish(ip, struct_obj.namespace);
- if (inst_info.inst == .main_struct_inst) {
- // This is the root type of a file! Update the reference.
- zcu.setFileRootType(inst_info.file, new_ty);
- }
- return new_ty;
-}
-
-fn recreateUnionType(
- pt: Zcu.PerThread,
- old_ty: InternPool.Index,
- key: InternPool.Key.NamespaceType.Declared,
-) Allocator.Error!InternPool.Index {
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const inst_info = key.zir_index.resolveFull(ip).?;
- const file = zcu.fileByIndex(inst_info.file);
- const zir = file.zir.?;
-
- assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
- const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
- assert(extended.opcode == .union_decl);
- const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
- var extra_index = extra.end;
-
- extra_index += @intFromBool(small.has_tag_type);
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- extra_index += @intFromBool(small.has_body_len);
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew`
-
- const union_obj = ip.loadUnionType(old_ty);
-
- const namespace_index = union_obj.namespace;
-
- const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{
- .flags = .{
- .layout = small.layout,
- .status = .none,
- .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
- .tagged
- else if (small.layout != .auto)
- .none
- else switch (true) { // TODO
- true => .safety,
- false => .none,
- },
- .any_aligned_fields = small.any_aligned_fields,
- .requires_comptime = .unknown,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
- .alignment = .none,
- },
- .fields_len = fields_len,
- .enum_tag_ty = .none, // set later
- .field_types = &.{}, // set later
- .field_aligns = &.{}, // set later
- .key = .{ .declared_owned_captures = .{
- .zir_index = key.zir_index,
- .captures = key.captures.owned,
- } },
- }, true)) {
- .wip => |wip| wip,
- .existing => unreachable, // we passed `replace_existing`
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- wip_ty.setName(ip, union_obj.name, union_obj.name_nav);
- try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index });
- zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
- // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive.
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
-
- codegen_type: {
- if (file.mod.?.strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
- try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
- }
-
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- return wip_ty.finish(ip, namespace_index);
-}
-
-/// This *does* call `Sema.resolveDeclaredEnum`, but errors from it are not propagated.
-/// Call sites are resposible for checking `[transitive_]failed_analysis` after `ensureTypeUpToDate`
-/// returns in order to detect resolution failures.
-fn recreateEnumType(
- pt: Zcu.PerThread,
- old_ty: InternPool.Index,
- key: InternPool.Key.NamespaceType.Declared,
-) (Allocator.Error || Io.Cancelable)!InternPool.Index {
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = comp.gpa;
- const io = comp.io;
- const ip = &zcu.intern_pool;
-
- const inst_info = key.zir_index.resolveFull(ip).?;
- const file = zcu.fileByIndex(inst_info.file);
- const zir = file.zir.?;
-
- assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
- const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
- assert(extended.opcode == .enum_decl);
- const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
- var extra_index = extra.end;
-
- const tag_type_ref = if (small.has_tag_type) blk: {
- const tag_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
- extra_index += 1;
- break :blk tag_type_ref;
- } else .none;
-
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
-
- const body_len = if (small.has_body_len) blk: {
- const body_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
-
- assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew`
-
- extra_index += captures_len * 2;
- extra_index += decls_len;
-
- const body = zir.bodySlice(extra_index, body_len);
- extra_index += body.len;
-
- const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
- const body_end = extra_index;
- extra_index += bit_bags_count;
-
- const any_values = for (zir.extra[body_end..][0..bit_bags_count]) |bag| {
- if (bag != 0) break true;
- } else false;
-
- const enum_obj = ip.loadEnumType(old_ty);
-
- const namespace_index = enum_obj.namespace;
-
- const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
- .has_values = any_values,
- .tag_mode = if (small.nonexhaustive)
- .nonexhaustive
- else if (tag_type_ref == .none)
- .auto
- else
- .explicit,
- .fields_len = fields_len,
- .key = .{ .declared_owned_captures = .{
- .zir_index = key.zir_index,
- .captures = key.captures.owned,
- } },
- }, true)) {
- .wip => |wip| wip,
- .existing => unreachable, // we passed `replace_existing`
- };
- var done = true;
- errdefer if (!done) wip_ty.cancel(ip, pt.tid);
-
- wip_ty.setName(ip, enum_obj.name, enum_obj.name_nav);
-
- zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
- // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive.
-
- if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
- wip_ty.prepare(ip, namespace_index);
- done = true;
-
- Sema.resolveDeclaredEnum(
- pt,
- wip_ty,
- inst_info.inst,
- key.zir_index,
- namespace_index,
- enum_obj.name,
- small,
- body,
- tag_type_ref,
- any_values,
- fields_len,
- zir,
- body_end,
- ) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.Canceled => |e| return e,
- error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this
- };
-
- return wip_ty.index;
-}
-
/// Given a namespace, re-scan its declarations from the type definition if they have not
/// yet been re-scanned on this update.
/// If the type declaration instruction has been lost, returns `error.AnalysisFail`.
@@ -4396,7 +4130,7 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
};
const key = switch (full_key) {
- .reified, .generated_tag => {
+ .reified, .generated_union_tag => {
// Namespace always empty, so up-to-date.
namespace.generation = zcu.generation;
return;
@@ -4408,100 +4142,13 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
const file = zcu.fileByIndex(inst_info.file);
- const zir = file.zir.?;
-
- assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
- const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
+ const zir = &file.zir.?;
const decls = switch (container) {
- .@"struct" => decls: {
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
- var extra_index = extra.end;
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- extra_index += @intFromBool(small.has_fields_len);
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- extra_index += captures_len * 2;
- if (small.has_backing_int) {
- const backing_int_body_len = zir.extra[extra_index];
- extra_index += 1; // backing_int_body_len
- if (backing_int_body_len == 0) {
- extra_index += 1; // backing_int_ref
- } else {
- extra_index += backing_int_body_len; // backing_int_body_inst
- }
- }
- break :decls zir.bodySlice(extra_index, decls_len);
- },
- .@"union" => decls: {
- assert(extended.opcode == .union_decl);
- const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
- var extra_index = extra.end;
- extra_index += @intFromBool(small.has_tag_type);
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- extra_index += @intFromBool(small.has_body_len);
- extra_index += @intFromBool(small.has_fields_len);
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- extra_index += captures_len * 2;
- break :decls zir.bodySlice(extra_index, decls_len);
- },
- .@"enum" => decls: {
- assert(extended.opcode == .enum_decl);
- const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
- var extra_index = extra.end;
- extra_index += @intFromBool(small.has_tag_type);
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- extra_index += @intFromBool(small.has_body_len);
- extra_index += @intFromBool(small.has_fields_len);
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- extra_index += captures_len * 2;
- break :decls zir.bodySlice(extra_index, decls_len);
- },
- .@"opaque" => decls: {
- assert(extended.opcode == .opaque_decl);
- const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
- const extra = zir.extraData(Zir.Inst.OpaqueDecl, extended.operand);
- var extra_index = extra.end;
- const captures_len = if (small.has_captures_len) blk: {
- const captures_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk captures_len;
- } else 0;
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- extra_index += captures_len * 2;
- break :decls zir.bodySlice(extra_index, decls_len);
- },
+ .@"struct" => zir.getStructDecl(inst_info.inst).decls,
+ .@"union" => zir.getUnionDecl(inst_info.inst).decls,
+ .@"enum" => zir.getEnumDecl(inst_info.inst).decls,
+ .@"opaque" => zir.getOpaqueDecl(inst_info.inst).decls,
};
try pt.scanNamespace(namespace_index, decls);
@@ -4509,7 +4156,7 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
}
pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index {
- const ptr_ty = (try pt.ptrTypeSema(.{
+ const ptr_ty = (try pt.ptrType(.{
.child = pt.zcu.intern_pool.typeOf(val),
.flags = .{
.alignment = .none,
@@ -4703,3 +4350,466 @@ fn printVerboseAir(
try air.write(w, pt, liveness);
try w.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)});
}
+
+// MLUGG TODO: these functions are all blatant hacks. See if I can remove them!
+pub fn resolveTypeForCodegen(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void {
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ if (ty.isGenericPoison()) return;
+ switch (ty.zigTypeTag(zcu)) {
+ .type,
+ .void,
+ .bool,
+ .noreturn,
+ .int,
+ .float,
+ .error_set,
+ .@"opaque",
+ .comptime_float,
+ .comptime_int,
+ .undefined,
+ .null,
+ .enum_literal,
+ => {},
+
+ .frame, .@"anyframe" => @panic("TODO resolveTypeForCodegen async frames"),
+
+ .optional => try pt.resolveTypeForCodegen(ty.childType(zcu)),
+ .error_union => try pt.resolveTypeForCodegen(ty.errorUnionPayload(zcu)),
+ .pointer => try pt.resolveTypeForCodegen(ty.childType(zcu)),
+ .array => try pt.resolveTypeForCodegen(ty.childType(zcu)),
+ .vector => try pt.resolveTypeForCodegen(ty.childType(zcu)),
+
+ .@"fn" => {
+ const info = zcu.typeToFunc(ty).?;
+ for (0..info.param_types.len) |i| {
+ const param_ty = info.param_types.get(ip)[i];
+ try pt.resolveTypeForCodegen(.fromInterned(param_ty));
+ }
+ try pt.resolveTypeForCodegen(.fromInterned(info.return_type));
+ },
+
+ .@"struct" => switch (ip.indexToKey(ty.toIntern())) {
+ .struct_type => {
+ try pt.ensureTypeLayoutUpToDate(ty);
+ try pt.ensureTypeInitsUpToDate(ty);
+ },
+ .tuple_type => |tuple| for (0..tuple.types.len) |i| {
+ const field_is_comptime = tuple.values.get(ip)[i] != .none;
+ if (field_is_comptime) continue;
+ const field_ty = tuple.types.get(ip)[i];
+ try pt.resolveTypeForCodegen(.fromInterned(field_ty));
+ },
+ else => unreachable,
+ },
+
+ .@"union" => try pt.ensureTypeLayoutUpToDate(ty),
+ .@"enum" => try pt.ensureTypeInitsUpToDate(ty),
+ }
+}
+pub fn resolveValueTypesForCodegen(pt: Zcu.PerThread, val: Value) Zcu.SemaError!void {
+ const zcu = pt.zcu;
+ const ty: Type = switch (val.typeOf(zcu).toIntern()) {
+ .type_type => if (val.isUndef(zcu)) {
+ return;
+ } else val.toType(),
+ else => |ty| .fromInterned(ty),
+ };
+ return pt.resolveTypeForCodegen(ty);
+}
+pub fn resolveAirTypesForCodegen(pt: Zcu.PerThread, air: *const Air) Zcu.SemaError!void {
+ return pt.resolveBodyTypesForCodegen(air, air.getMainBody());
+}
+fn resolveBodyTypesForCodegen(pt: Zcu.PerThread, air: *const Air, body: []const Air.Inst.Index) Zcu.SemaError!void {
+ const zcu = pt.zcu;
+ const tags = air.instructions.items(.tag);
+ const datas = air.instructions.items(.data);
+ for (body) |inst| {
+ const data = datas[@intFromEnum(inst)];
+ switch (tags[@intFromEnum(inst)]) {
+ .inferred_alloc, .inferred_alloc_comptime => unreachable,
+
+ .arg => try pt.resolveTypeForCodegen(data.arg.ty.toType()),
+
+ .add,
+ .add_safe,
+ .add_optimized,
+ .add_wrap,
+ .add_sat,
+ .sub,
+ .sub_safe,
+ .sub_optimized,
+ .sub_wrap,
+ .sub_sat,
+ .mul,
+ .mul_safe,
+ .mul_optimized,
+ .mul_wrap,
+ .mul_sat,
+ .div_float,
+ .div_float_optimized,
+ .div_trunc,
+ .div_trunc_optimized,
+ .div_floor,
+ .div_floor_optimized,
+ .div_exact,
+ .div_exact_optimized,
+ .rem,
+ .rem_optimized,
+ .mod,
+ .mod_optimized,
+ .max,
+ .min,
+ .bit_and,
+ .bit_or,
+ .shr,
+ .shr_exact,
+ .shl,
+ .shl_exact,
+ .shl_sat,
+ .xor,
+ .cmp_lt,
+ .cmp_lt_optimized,
+ .cmp_lte,
+ .cmp_lte_optimized,
+ .cmp_eq,
+ .cmp_eq_optimized,
+ .cmp_gte,
+ .cmp_gte_optimized,
+ .cmp_gt,
+ .cmp_gt_optimized,
+ .cmp_neq,
+ .cmp_neq_optimized,
+ .bool_and,
+ .bool_or,
+ .store,
+ .store_safe,
+ .set_union_tag,
+ .array_elem_val,
+ .slice_elem_val,
+ .ptr_elem_val,
+ .memset,
+ .memset_safe,
+ .memcpy,
+ .memmove,
+ .atomic_store_unordered,
+ .atomic_store_monotonic,
+ .atomic_store_release,
+ .atomic_store_seq_cst,
+ .legalize_vec_elem_val,
+ => {
+ try pt.resolveRefTypesForCodegen(data.bin_op.lhs);
+ try pt.resolveRefTypesForCodegen(data.bin_op.rhs);
+ },
+
+ .not,
+ .bitcast,
+ .clz,
+ .ctz,
+ .popcount,
+ .byte_swap,
+ .bit_reverse,
+ .abs,
+ .load,
+ .fptrunc,
+ .fpext,
+ .intcast,
+ .intcast_safe,
+ .trunc,
+ .optional_payload,
+ .optional_payload_ptr,
+ .optional_payload_ptr_set,
+ .wrap_optional,
+ .unwrap_errunion_payload,
+ .unwrap_errunion_err,
+ .unwrap_errunion_payload_ptr,
+ .unwrap_errunion_err_ptr,
+ .errunion_payload_ptr_set,
+ .wrap_errunion_payload,
+ .wrap_errunion_err,
+ .struct_field_ptr_index_0,
+ .struct_field_ptr_index_1,
+ .struct_field_ptr_index_2,
+ .struct_field_ptr_index_3,
+ .get_union_tag,
+ .slice_len,
+ .slice_ptr,
+ .ptr_slice_len_ptr,
+ .ptr_slice_ptr_ptr,
+ .array_to_slice,
+ .int_from_float,
+ .int_from_float_optimized,
+ .int_from_float_safe,
+ .int_from_float_optimized_safe,
+ .float_from_int,
+ .splat,
+ .error_set_has_value,
+ .addrspace_cast,
+ .c_va_arg,
+ .c_va_copy,
+ => {
+ try pt.resolveTypeForCodegen(data.ty_op.ty.toType());
+ try pt.resolveRefTypesForCodegen(data.ty_op.operand);
+ },
+
+ .alloc,
+ .ret_ptr,
+ .c_va_start,
+ => try pt.resolveTypeForCodegen(data.ty),
+
+ .ptr_add,
+ .ptr_sub,
+ .add_with_overflow,
+ .sub_with_overflow,
+ .mul_with_overflow,
+ .shl_with_overflow,
+ .slice,
+ .slice_elem_ptr,
+ .ptr_elem_ptr,
+ => {
+ const bin = air.extraData(Air.Bin, data.ty_pl.payload).data;
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ try pt.resolveRefTypesForCodegen(bin.lhs);
+ try pt.resolveRefTypesForCodegen(bin.rhs);
+ },
+
+ .block,
+ .loop,
+ => {
+ const block = air.unwrapBlock(inst);
+ try pt.resolveTypeForCodegen(block.ty);
+ try pt.resolveBodyTypesForCodegen(air, block.body);
+ },
+
+ .dbg_inline_block => {
+ const block = air.unwrapDbgBlock(inst);
+ try pt.resolveTypeForCodegen(block.ty);
+ try pt.resolveBodyTypesForCodegen(air, block.body);
+ },
+
+ .sqrt,
+ .sin,
+ .cos,
+ .tan,
+ .exp,
+ .exp2,
+ .log,
+ .log2,
+ .log10,
+ .floor,
+ .ceil,
+ .round,
+ .trunc_float,
+ .neg,
+ .neg_optimized,
+ .is_null,
+ .is_non_null,
+ .is_null_ptr,
+ .is_non_null_ptr,
+ .is_err,
+ .is_non_err,
+ .is_err_ptr,
+ .is_non_err_ptr,
+ .ret,
+ .ret_safe,
+ .ret_load,
+ .is_named_enum_value,
+ .tag_name,
+ .error_name,
+ .cmp_lt_errors_len,
+ .c_va_end,
+ .set_err_return_trace,
+ => try pt.resolveRefTypesForCodegen(data.un_op),
+
+ .br, .switch_dispatch => try pt.resolveRefTypesForCodegen(data.br.operand),
+
+ .cmp_vector,
+ .cmp_vector_optimized,
+ => {
+ const extra = air.extraData(Air.VectorCmp, data.ty_pl.payload).data;
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ try pt.resolveRefTypesForCodegen(extra.lhs);
+ try pt.resolveRefTypesForCodegen(extra.rhs);
+ },
+
+ .reduce,
+ .reduce_optimized,
+ => try pt.resolveRefTypesForCodegen(data.reduce.operand),
+
+ .struct_field_ptr,
+ .struct_field_val,
+ => {
+ const extra = air.extraData(Air.StructField, data.ty_pl.payload).data;
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ try pt.resolveRefTypesForCodegen(extra.struct_operand);
+ },
+
+ .shuffle_one => {
+ const unwrapped = air.unwrapShuffleOne(zcu, inst);
+ try pt.resolveTypeForCodegen(unwrapped.result_ty);
+ try pt.resolveRefTypesForCodegen(unwrapped.operand);
+ for (unwrapped.mask) |m| switch (m.unwrap()) {
+ .elem => {},
+ .value => |val| try pt.resolveValueTypesForCodegen(.fromInterned(val)),
+ };
+ },
+
+ .shuffle_two => {
+ const unwrapped = air.unwrapShuffleTwo(zcu, inst);
+ try pt.resolveTypeForCodegen(unwrapped.result_ty);
+ try pt.resolveRefTypesForCodegen(unwrapped.operand_a);
+ try pt.resolveRefTypesForCodegen(unwrapped.operand_b);
+ // No values to check because there are no comptime-known values other than undef
+ },
+
+ .cmpxchg_weak,
+ .cmpxchg_strong,
+ => {
+ const extra = air.extraData(Air.Cmpxchg, data.ty_pl.payload).data;
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ try pt.resolveRefTypesForCodegen(extra.ptr);
+ try pt.resolveRefTypesForCodegen(extra.expected_value);
+ try pt.resolveRefTypesForCodegen(extra.new_value);
+ },
+
+ .aggregate_init => {
+ const ty = data.ty_pl.ty.toType();
+ const elems_len: usize = @intCast(ty.arrayLen(zcu));
+ const elems: []const Air.Inst.Ref = @ptrCast(air.extra.items[data.ty_pl.payload..][0..elems_len]);
+ try pt.resolveTypeForCodegen(ty);
+ if (ty.zigTypeTag(zcu) == .@"struct") {
+ for (elems, 0..) |elem, elem_idx| {
+ if (ty.structFieldIsComptime(elem_idx, zcu)) continue;
+ try pt.resolveRefTypesForCodegen(elem);
+ }
+ } else {
+ for (elems) |elem| {
+ try pt.resolveRefTypesForCodegen(elem);
+ }
+ }
+ },
+
+ .union_init => {
+ const extra = air.extraData(Air.UnionInit, data.ty_pl.payload).data;
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ try pt.resolveRefTypesForCodegen(extra.init);
+ },
+
+ .field_parent_ptr => {
+ const extra = air.extraData(Air.FieldParentPtr, data.ty_pl.payload).data;
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ try pt.resolveRefTypesForCodegen(extra.field_ptr);
+ },
+
+ .atomic_load => try pt.resolveRefTypesForCodegen(data.atomic_load.ptr),
+
+ .prefetch => try pt.resolveRefTypesForCodegen(data.prefetch.ptr),
+
+ .runtime_nav_ptr => try pt.resolveTypeForCodegen(.fromInterned(data.ty_nav.ty)),
+
+ .select,
+ .mul_add,
+ .legalize_vec_store_elem,
+ => {
+ const bin = air.extraData(Air.Bin, data.pl_op.payload).data;
+ try pt.resolveRefTypesForCodegen(data.pl_op.operand);
+ try pt.resolveRefTypesForCodegen(bin.lhs);
+ try pt.resolveRefTypesForCodegen(bin.rhs);
+ },
+
+ .atomic_rmw => {
+ const extra = air.extraData(Air.AtomicRmw, data.pl_op.payload).data;
+ try pt.resolveRefTypesForCodegen(data.pl_op.operand);
+ try pt.resolveRefTypesForCodegen(extra.operand);
+ },
+
+ .call,
+ .call_always_tail,
+ .call_never_tail,
+ .call_never_inline,
+ => {
+ const call = air.unwrapCall(inst);
+ try pt.resolveRefTypesForCodegen(call.callee);
+ for (call.args) |arg| try pt.resolveRefTypesForCodegen(arg);
+ },
+
+ .dbg_var_ptr,
+ .dbg_var_val,
+ .dbg_arg_inline,
+ => try pt.resolveRefTypesForCodegen(data.pl_op.operand),
+
+ .@"try", .try_cold => {
+ const @"try" = air.unwrapTry(inst);
+ try pt.resolveRefTypesForCodegen(@"try".error_union);
+ try pt.resolveBodyTypesForCodegen(air, @"try".else_body);
+ },
+
+ .try_ptr, .try_ptr_cold => {
+ const try_ptr = air.unwrapTryPtr(inst);
+ try pt.resolveTypeForCodegen(try_ptr.error_union_payload_ptr_ty.toType());
+ try pt.resolveRefTypesForCodegen(try_ptr.error_union_ptr);
+ try pt.resolveBodyTypesForCodegen(air, try_ptr.else_body);
+ },
+
+ .cond_br => {
+ const cond_br = air.unwrapCondBr(inst);
+ try pt.resolveRefTypesForCodegen(cond_br.condition);
+ try pt.resolveBodyTypesForCodegen(air, cond_br.then_body);
+ try pt.resolveBodyTypesForCodegen(air, cond_br.else_body);
+ },
+
+ .switch_br, .loop_switch_br => {
+ const switch_br = air.unwrapSwitch(inst);
+ try pt.resolveRefTypesForCodegen(switch_br.operand);
+ var it = switch_br.iterateCases();
+ while (it.next()) |case| {
+ for (case.items) |item| {
+ try pt.resolveRefTypesForCodegen(item);
+ }
+ for (case.ranges) |range| {
+ try pt.resolveRefTypesForCodegen(range[0]);
+ try pt.resolveRefTypesForCodegen(range[1]);
+ }
+ try pt.resolveBodyTypesForCodegen(air, case.body);
+ }
+ try pt.resolveBodyTypesForCodegen(air, it.elseBody());
+ },
+
+ .assembly => {
+ const @"asm" = air.unwrapAsm(inst);
+ try pt.resolveTypeForCodegen(data.ty_pl.ty.toType());
+ for (@"asm".outputs) |output| if (output != .none) try pt.resolveRefTypesForCodegen(output);
+ for (@"asm".inputs) |input| if (input != .none) try pt.resolveRefTypesForCodegen(input);
+ },
+
+ .legalize_compiler_rt_call => {
+ const compiler_rt_call = air.unwrapCompilerRtCall(inst);
+ for (compiler_rt_call.args) |arg| try pt.resolveRefTypesForCodegen(arg);
+ },
+
+ .trap,
+ .breakpoint,
+ .ret_addr,
+ .frame_addr,
+ .unreach,
+ .wasm_memory_size,
+ .wasm_memory_grow,
+ .work_item_id,
+ .work_group_size,
+ .work_group_id,
+ .dbg_stmt,
+ .dbg_empty_stmt,
+ .err_return_trace,
+ .save_err_return_trace_index,
+ .repeat,
+ => {},
+ }
+ }
+}
+fn resolveRefTypesForCodegen(pt: Zcu.PerThread, ref: Air.Inst.Ref) Zcu.SemaError!void {
+ const ip_index = ref.toInterned() orelse {
+ // `ref` refers to a prior instruction, which we already did the resolution for.
+ return;
+ };
+ return pt.resolveValueTypesForCodegen(.fromInterned(ip_index));
+}
diff --git a/src/codegen.zig b/src/codegen.zig
index 6bdfa32f45f277ecd40d18e990f5f7219b81f5e4..176649f5b3d90bfca38ab60da0877734440761ee 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -1088,7 +1088,7 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo
return .{ .immediate = fn_ty.abiAlignment(zcu).toByteUnits().? };
}
} else if (ty.zigTypeTag(zcu) == .pointer) {
- const elem_ty = ty.elemType2(zcu);
+ const elem_ty = ty.childType(zcu);
if (!elem_ty.hasRuntimeBits(zcu)) {
return .{ .immediate = elem_ty.abiAlignment(zcu).toByteUnits().? };
}
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index 55f0d7fcc0e37b35198f01c2ff88c96cfd72aca4..0e6387949aa8d337f9c731db050551f710fa31e9 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -2464,7 +2464,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_pl = air.data(air.inst_index).ty_pl;
const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
- const elem_size = ty_pl.ty.toType().elemType2(zcu).abiSize(zcu);
+ const elem_size = ty_pl.ty.toType().childType(zcu).abiSize(zcu);
const base_vi = try isel.use(bin_op.lhs);
var base_part_it = base_vi.field(ty_pl.ty.toType(), 0, 8);
@@ -6145,7 +6145,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
} else {
const elem_ptr_ra = try isel.allocIntReg();
defer isel.freeReg(elem_ptr_ra);
- if (!try elem_vi.value.load(isel, slice_ty.elemType2(zcu), elem_ptr_ra, .{
+ if (!try elem_vi.value.load(isel, slice_ty.childType(zcu), elem_ptr_ra, .{
.@"volatile" = ptr_info.flags.is_volatile,
})) break :unused;
const slice_vi = try isel.use(bin_op.lhs);
@@ -6253,7 +6253,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
} else {
const elem_ptr_ra = try isel.allocIntReg();
defer isel.freeReg(elem_ptr_ra);
- if (!try elem_vi.value.load(isel, ptr_ty.elemType2(zcu), elem_ptr_ra, .{
+ if (!try elem_vi.value.load(isel, ptr_ty.childType(zcu), elem_ptr_ra, .{
.@"volatile" = ptr_info.flags.is_volatile,
})) break :unused;
const base_vi = try isel.use(bin_op.lhs);
@@ -6594,7 +6594,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (try isel.hasRepeatedByteRepr(.fromInterned(fill_val))) |fill_byte|
break :fill_byte .{ .constant = fill_byte };
}
- switch (dst_ty.elemType2(zcu).abiSize(zcu)) {
+ switch (dst_ty.indexablePtrElem(zcu).abiSize(zcu)) {
0 => unreachable,
1 => break :fill_byte .{ .value = bin_op.rhs },
2, 4, 8 => |size| {
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 106737a8331c523f9ecddad0e0138f31a10ea06f..831a64779bc423595b5433b4d70eaf8a5c0fbf37 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -3676,7 +3676,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
const inst_ty = f.typeOfIndex(inst);
const ptr_ty = f.typeOf(bin_op.lhs);
- const elem_has_bits = ptr_ty.elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu);
+ const elem_has_bits = ptr_ty.indexablePtrElem(zcu).hasRuntimeBitsIgnoreComptime(zcu);
const ptr = try f.resolveInst(bin_op.lhs);
const index = try f.resolveInst(bin_op.rhs);
@@ -3738,7 +3738,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
const inst_ty = f.typeOfIndex(inst);
const slice_ty = f.typeOf(bin_op.lhs);
- const elem_ty = slice_ty.elemType2(zcu);
+ const elem_ty = slice_ty.childType(zcu);
const elem_has_bits = elem_ty.hasRuntimeBitsIgnoreComptime(zcu);
const slice = try f.resolveInst(bin_op.lhs);
@@ -4502,7 +4502,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
const inst_ty = f.typeOfIndex(inst);
const inst_scalar_ty = inst_ty.scalarType(zcu);
- const elem_ty = inst_scalar_ty.elemType2(zcu);
+ const elem_ty = inst_scalar_ty.indexablePtrElem(zcu);
if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return f.moveCValue(inst, inst_ty, lhs);
const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
@@ -7037,7 +7037,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV
try w.writeAll(", ");
try writeArrayLen(f, dest_ptr, dest_ty);
try w.writeAll(" * sizeof(");
- try f.renderType(w, dest_ty.elemType2(zcu));
+ try f.renderType(w, dest_ty.indexablePtrElem(zcu));
try w.writeAll("));");
try f.object.newline();
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 4ef72e8ab7fed1786845aa5b8e08d1ddfe6091fa..1327b7b2e1c9985b30f53c4411a8fcf8afa9f89c 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -2112,7 +2112,7 @@ pub const Object = struct {
return debug_array_type;
},
.vector => {
- const elem_ty = ty.elemType2(zcu);
+ const elem_ty = ty.childType(zcu);
// Vector elements cannot be padded since that would make
// @bitSizOf(elem) * len > @bitSizOf(vec).
// Neither gdb nor lldb seem to be able to display non-byte sized
diff --git a/src/codegen/mips/abi.zig b/src/codegen/mips/abi.zig
index 02c4c637a4c362ea4b3a826fbd27b72598fcc2cb..6678b74ebc32dceed774331667cdf9d498855947 100644
--- a/src/codegen/mips/abi.zig
+++ b/src/codegen/mips/abi.zig
@@ -44,7 +44,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
return .byval;
},
.vector => {
- const elem_type = ty.elemType2(zcu);
+ const elem_type = ty.childType(zcu);
switch (elem_type.zigTypeTag(zcu)) {
.bool, .int => {
const bit_size = ty.bitSize(zcu);
diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig
index 1f70f5f4de379ad0e01f75be35f0d13695f5b766..dd4ca3f88bbc6bc082a00ab1e62a1b685e068e06 100644
--- a/src/codegen/riscv64/CodeGen.zig
+++ b/src/codegen/riscv64/CodeGen.zig
@@ -2673,7 +2673,7 @@ fn genBinOp(
defer func.register_manager.unlockReg(tmp_lock);
// RISC-V has no immediate mul, so we copy the size to a temporary register
- const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu);
+ const elem_size = lhs_ty.indexablePtrElem(zcu).abiSize(zcu);
const elem_size_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = elem_size });
try func.genBinOp(
@@ -3913,9 +3913,8 @@ fn airPtrElemVal(func: *Func, inst: Air.Inst.Index) !void {
const base_ptr_ty = func.typeOf(bin_op.lhs);
const result: MCValue = if (!is_volatile and func.liveness.isUnused(inst)) .unreach else result: {
- const elem_ty = base_ptr_ty.elemType2(zcu);
+ const elem_ty = base_ptr_ty.indexablePtrElem(zcu);
if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none;
-
const base_ptr_mcv = try func.resolveInst(bin_op.lhs);
const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig
index 2222bb9a050341495a7ca97f6b5a4b9dd5b189ec..e6850df250db68f6b34050f93cb19717ff90ecb2 100644
--- a/src/codegen/spirv/CodeGen.zig
+++ b/src/codegen/spirv/CodeGen.zig
@@ -4381,7 +4381,7 @@ fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id {
const zcu = cg.module.zcu;
// Construct new pointer type for the resulting pointer
- const elem_ty = ptr_ty.elemType2(zcu); // use elemType() so that we get T for *[N]T.
+ const elem_ty = ptr_ty.indexablePtrElem(zcu);
const elem_ty_id = try cg.resolveType(elem_ty, .indirect);
const elem_ptr_ty_id = try cg.module.ptrType(elem_ty_id, cg.module.storageClass(ptr_ty.ptrAddressSpace(zcu)));
if (ptr_ty.isSinglePointer(zcu)) {
diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig
index 6144a421c8cb132aebd77f745bff78618a3e7a0f..58987558d218402d252b81ff51901b85bcd74f78 100644
--- a/src/codegen/x86_64/CodeGen.zig
+++ b/src/codegen/x86_64/CodeGen.zig
@@ -43261,7 +43261,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
var res: [1]Temp = undefined;
- if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{
+ if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{
.patterns = &.{
.{ .src = .{ .to_gpr, .simm32, .none } },
},
@@ -43375,7 +43375,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
var res: [1]Temp = undefined;
- if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{
+ if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{
.patterns = &.{
.{ .src = .{ .to_gpr, .simm32, .none } },
},
@@ -103926,7 +103926,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.array_elem_val, .legalize_vec_elem_val => {
const bin_op = air_datas[@intFromEnum(inst)].bin_op;
const array_ty = cg.typeOf(bin_op.lhs);
- const res_ty = array_ty.elemType2(zcu);
+ const res_ty = array_ty.childType(zcu);
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
var res: [1]Temp = undefined;
cg.select(&res, &.{res_ty}, &ops, comptime &.{ .{
@@ -104121,7 +104121,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
},
.slice_elem_val, .ptr_elem_val => {
const bin_op = air_datas[@intFromEnum(inst)].bin_op;
- const res_ty = cg.typeOf(bin_op.lhs).elemType2(zcu);
+ const res_ty = cg.typeOf(bin_op.lhs).indexablePtrElem(zcu);
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
var res: [1]Temp = undefined;
@@ -187919,7 +187919,6 @@ const Select = struct {
unsigned_int: Memory.Size,
elem_size_is: u8,
po2_elem_size,
- elem_int: Memory.Size,
const OfIsSizes = struct { of: Memory.Size, is: Memory.Size };
@@ -188178,12 +188177,8 @@ const Select = struct {
.signed => false,
.unsigned => size.bitSize(cg.target) >= int_info.bits,
} else false,
- .elem_size_is => |size| size == ty.elemType2(zcu).abiSize(zcu),
- .po2_elem_size => std.math.isPowerOfTwo(ty.elemType2(zcu).abiSize(zcu)),
- .elem_int => |size| if (cg.intInfo(ty.elemType2(zcu))) |elem_int_info|
- size.bitSize(cg.target) >= elem_int_info.bits
- else
- false,
+ .elem_size_is => |size| size == ty.indexablePtrElem(zcu).abiSize(zcu),
+ .po2_elem_size => std.math.isPowerOfTwo(ty.indexablePtrElem(zcu).abiSize(zcu)),
};
}
};
@@ -189918,20 +189913,20 @@ const Select = struct {
.dst0_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).abiSize(s.cg.pt.zcu)),
.delta_size => @intCast(@as(SignedImm, @intCast(op.flags.base.ref.typeOf(s).abiSize(s.cg.pt.zcu))) -
@as(SignedImm, @intCast(op.flags.index.ref.typeOf(s).abiSize(s.cg.pt.zcu)))),
- .delta_elem_size => @intCast(@as(SignedImm, @intCast(op.flags.base.ref.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))) -
- @as(SignedImm, @intCast(op.flags.index.ref.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)))),
+ .delta_elem_size => @intCast(@as(SignedImm, @intCast(op.flags.base.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))) -
+ @as(SignedImm, @intCast(op.flags.index.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)))),
.unaligned_size => @intCast(s.cg.unalignedSize(op.flags.base.ref.typeOf(s))),
.unaligned_size_add_elem_size => {
const ty = op.flags.base.ref.typeOf(s);
- break :lhs @intCast(s.cg.unalignedSize(ty) + ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu));
+ break :lhs @intCast(s.cg.unalignedSize(ty) + ty.scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu));
},
.unaligned_size_sub_elem_size => {
const ty = op.flags.base.ref.typeOf(s);
- break :lhs @intCast(s.cg.unalignedSize(ty) - ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu));
+ break :lhs @intCast(s.cg.unalignedSize(ty) - ty.scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu));
},
.unaligned_size_sub_2_elem_size => {
const ty = op.flags.base.ref.typeOf(s);
- break :lhs @intCast(s.cg.unalignedSize(ty) - ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * 2);
+ break :lhs @intCast(s.cg.unalignedSize(ty) - ty.scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * 2);
},
.bit_size => @intCast(s.cg.nonBoolScalarBitSize(op.flags.base.ref.typeOf(s))),
.src0_bit_size => @intCast(s.cg.nonBoolScalarBitSize(Select.Operand.Ref.src0.typeOf(s))),
@@ -189944,10 +189939,10 @@ const Select = struct {
op.flags.base.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu),
@divExact(op.flags.base.size.bitSize(s.cg.target), 8),
)),
- .elem_size => @intCast(op.flags.base.ref.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)),
- .src0_elem_size => @intCast(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)),
- .dst0_elem_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)),
- .src0_elem_size_mul_src1 => @intCast(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) *
+ .elem_size => @intCast(op.flags.base.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)),
+ .src0_elem_size => @intCast(Select.Operand.Ref.src0.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)),
+ .dst0_elem_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)),
+ .src0_elem_size_mul_src1 => @intCast(Select.Operand.Ref.src0.typeOf(s).indexableElem(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) *
Select.Operand.Ref.src1.valueOf(s).immediate),
.vector_index => switch (op.flags.base.ref.typeOf(s).ptrInfo(s.cg.pt.zcu).flags.vector_index) {
.none => unreachable,
@@ -189956,7 +189951,7 @@ const Select = struct {
.src1 => @intCast(Select.Operand.Ref.src1.valueOf(s).immediate),
.src1_sub_bit_size => @as(SignedImm, @intCast(Select.Operand.Ref.src1.valueOf(s).immediate)) -
@as(SignedImm, @intCast(s.cg.nonBoolScalarBitSize(op.flags.base.ref.typeOf(s)))),
- .log2_src0_elem_size => @intCast(std.math.log2(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))),
+ .log2_src0_elem_size => @intCast(std.math.log2(Select.Operand.Ref.src0.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))),
.elem_mask => @as(u8, std.math.maxInt(u8)) >> @intCast(
8 - ((s.cg.unalignedSize(op.flags.base.ref.typeOf(s)) - 1) %
@divExact(op.flags.base.size.bitSize(s.cg.target), 8) + 1 >>
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
index 19bbee45b3a12d6883f3e7803475eb960c5a1784..fdd516ff17bc3a2349580cde73a179daa2f51523 100644
--- a/src/link/Dwarf.zig
+++ b/src/link/Dwarf.zig
@@ -4575,10 +4575,10 @@ fn updateContainerTypeWriterError(
const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) {
.struct_init, .struct_init_ref, .struct_init_anon => .anon,
.extended => switch (decl_inst.data.extended.opcode) {
- .struct_decl => @as(Zir.Inst.StructDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
- .enum_decl => @as(Zir.Inst.EnumDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
- .union_decl => @as(Zir.Inst.UnionDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
- .opaque_decl => @as(Zir.Inst.OpaqueDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
+ .struct_decl => file.zir.?.getStructDecl(inst_info.inst).name_strategy,
+ .union_decl => file.zir.?.getUnionDecl(inst_info.inst).name_strategy,
+ .enum_decl => file.zir.?.getEnumDecl(inst_info.inst).name_strategy,
+ .opaque_decl => file.zir.?.getOpaqueDecl(inst_info.inst).name_strategy,
.reify_enum,
.reify_struct,
diff --git a/src/mutable_value.zig b/src/mutable_value.zig
index c9eb993944e15366de31846611b76cd070064fe9..97d6f22b3b90e5d9215ec011760ee6feb9c0d3f1 100644
--- a/src/mutable_value.zig
+++ b/src/mutable_value.zig
@@ -18,7 +18,7 @@ pub const MutableValue = union(enum) {
opt_payload: SubValue,
/// An aggregate consisting of a single repeated value.
repeated: SubValue,
- /// An aggregate of `u8` consisting of "plain" bytes (no lazy or undefined elements).
+ /// An aggregate of `u8` consisting of "plain" bytes (no undefined elements).
bytes: Bytes,
/// An aggregate with arbitrary sub-values.
aggregate: Aggregate,
@@ -415,16 +415,7 @@ pub const MutableValue = union(enum) {
} else if (!is_struct and is_trivial_int and Type.fromInterned(a.ty).childType(zcu).toIntern() == .u8_type) {
// See if we can switch to `bytes` repr
for (a.elems) |e| {
- switch (e) {
- else => break,
- .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
- else => break,
- .int => |int| switch (int.storage) {
- .u64, .i64, .big_int => {},
- .lazy_align, .lazy_size => break,
- },
- },
- }
+ if (!e.isTrivialInt(zcu)) break;
} else {
const bytes = try arena.alloc(u8, a.elems.len);
for (a.elems, bytes) |elem_val, *b| {
@@ -494,10 +485,7 @@ pub const MutableValue = union(enum) {
else => false,
.interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) {
else => false,
- .int => |int| switch (int.storage) {
- .u64, .i64, .big_int => true,
- .lazy_align, .lazy_size => false,
- },
+ .int => true,
},
};
}
diff --git a/src/print_value.zig b/src/print_value.zig
index 28c25954272bccaa8069ff570043e34625236c6c..e58288a16a999c3ee2c17dbd3b31fd8fec4a59d9 100644
--- a/src/print_value.zig
+++ b/src/print_value.zig
@@ -81,14 +81,6 @@ pub fn print(
.int => |int| switch (int.storage) {
inline .u64, .i64 => |x| try writer.print("{d}", .{x}),
.big_int => |x| try writer.print("{d}", .{x}),
- .lazy_align => |ty| if (opt_sema != null) {
- const a = try Type.fromInterned(ty).abiAlignmentSema(pt);
- try writer.print("{d}", .{a.toByteUnits() orelse 0});
- } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
- .lazy_size => |ty| if (opt_sema != null) {
- const s = try Type.fromInterned(ty).abiSizeSema(pt);
- try writer.print("{d}", .{s});
- } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
},
.err => |err| try writer.print("error.{f}", .{
err.name.fmt(ip),
@@ -104,8 +96,8 @@ pub fn print(
}),
.enum_tag => |enum_tag| {
const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());
- if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| {
- return writer.print(".{f}", .{enum_type.names.get(ip)[tag_index].fmt(ip)});
+ if (enum_type.tagValueIndex(ip, enum_tag.int)) |tag_index| {
+ return writer.print(".{f}", .{enum_type.field_names.get(ip)[tag_index].fmt(ip)});
}
if (level == 0) {
return writer.writeAll("@enumFromInt(...)");