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(...)");